BUG #15572: Misleading message reported by "Drop function operation" on DB with functions having same name
The following bug has been logged on the website:
Bug reference: 15572
Logged by: Ash Marath
Email address: makmarath@hotmail.com
PostgreSQL version: 10.5
Operating system: RDS (on Amazon)
Description:
Scenario:
DB has 2 functions with same name.
DB: testDB
Schema: test
Function 1: test.func1(param1 text, param2 text)
Function 2: test.func1(param1 text)
---------------------------------
Issue: Misleading message reported by "DROP FUNCTION" command with the above
scenario
Step 1:
Run the command : DROP FUNCTION test.func1;
NOTE: This operation failed to execute the drop and reported the following
message
Message reported by PgAdmin4 & OmniDB:
---- start of message ------
function name "test.func1" is not unique
HINT: Specify the argument list to select the function
unambiguously.
---- end of message ------
--------------------------------------------------------------------------------------------------------
Step 2:
Run the command : DROP FUNCTION IF EXISTS test.func1;
NOTE: This operation completed successfully without error and reported the
following message
Message reported by PgAdmin4 & OmniDB:
---- start of message ------
function admq.test1() does not exist, skipping
---- end of message ------
-----------------------------------------------------------------------------------------------------------
Proposed solution:
The operation in Step 2 should have failed with the same error as reported
in Step 1;
Thanks
Ash Marath
makmarath@hotmail.com
On Fri, 4 Jan 2019 at 09:44, PG Bug reporting form
<noreply@postgresql.org> wrote:
Operating system: RDS (on Amazon)
You may want to talk to Amazon about this. However, since the same
behaviour exists in PostgreSQL too...
Run the command : DROP FUNCTION test.func1;
NOTE: This operation failed to execute the drop and reported the following
messageMessage reported by PgAdmin4 & OmniDB:
---- start of message ------
function name "test.func1" is not unique
HINT: Specify the argument list to select the function
unambiguously.
---- end of message ------
Run the command : DROP FUNCTION IF EXISTS test.func1;
NOTE: This operation completed successfully without error and reported the
following messageMessage reported by PgAdmin4 & OmniDB:
---- start of message ------
function admq.test1() does not exist, skipping
---- end of message ------
-----------------------------------------------------------------------------------------------------------
Proposed solution:
The operation in Step 2 should have failed with the same error as reported
in Step 1;
It's not really that clear to me that doing that would be any more
correct than the alternative. If we changed the behaviour of this then
someone might equally come along later and complain that they
specified "IF EXISTS" and got an error. Maintaining the status quo at
least has the benefit of not randomly changing the behaviour because
it didn't suit one particular use case. The patch to change the
behaviour is pretty trivial and amounts to removing a single line of
code:
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 4661fc4f62..a9912b0986 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2053,12 +2053,11 @@ LookupFuncName(List *funcname, int nargs,
const Oid *argtypes, bool noError)
{
if (clist->next)
{
- if (!noError)
- ereport(ERROR,
-
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
-
errmsg("function name \"%s\" is not unique",
-
NameListToString(funcname)),
-
errhint("Specify the argument list to select the function
unambiguously.")));
+ ereport(ERROR,
+
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+
errmsg("function name \"%s\" is not unique",
+
NameListToString(funcname)),
+
errhint("Specify the argument list to select the function
unambiguously.")));
}
else
return clist->oid;
I just don't know if we'll have a better database by removing it.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Thursday, January 3, 2019, David Rowley <david.rowley@2ndquadrant.com>
wrote:
If we changed the behaviour of this then
someone might equally come along later and complain that they
specified "IF EXISTS" and got an error.
I’m inclined to argue that the docs say you can only use the omitted-args
name if it is unique within the schema. Since the second case is using
that form in violation of that requirement reporting an error would match
the documentation.
IF EXISTS only applies when no functions exist; an error for ambiguity
doesn’t violate its promise; and likely even if we didn’t make it an error
something else will fail later on.
It is wrong for the drop function if exists command to translate/print the
omitted-args form of the name into a function with zero arguments; it
should not be looking explicitly for a zero-arg function as it is not the
same thing (as emphasized in the docs).
So, I vote for changing this in 12 but leaving prior versions as-is for
compatability as the harm doesn’t seem to be enough to risk breakage.
Might be worth a doc patch showing the second case for the back branches
(Head seems like it would be good as we are fixing the code to match the
documentation, IMO).
David J.
Your Concern is valid as well for "IF exists" complaint from users (its a possibility ):
Then I would propose:
1. Either word the return message identical to the drop command message(without the "If Exists") & successfully pass the command.
OR
2. Fail the execution since just using the function name without parameters returns ambiguous results for the Drop to continue.
OR
3. Drop all functions with that function name & successfully pass the command.
With your comment the 1st option looks as a better option.
Regards
Ash
A Marath.
________________________________
From: David Rowley <david.rowley@2ndquadrant.com>
Sent: Thursday, January 3, 2019 11:45:16 PM
To: makmarath@hotmail.com; pgsql-bugs@lists.postgresql.org
Subject: Re: BUG #15572: Misleading message reported by "Drop function operation" on DB with functions having same name
On Fri, 4 Jan 2019 at 09:44, PG Bug reporting form
<noreply@postgresql.org> wrote:
Operating system: RDS (on Amazon)
You may want to talk to Amazon about this. However, since the same
behaviour exists in PostgreSQL too...
Run the command : DROP FUNCTION test.func1;
NOTE: This operation failed to execute the drop and reported the following
messageMessage reported by PgAdmin4 & OmniDB:
---- start of message ------
function name "test.func1" is not unique
HINT: Specify the argument list to select the function
unambiguously.
---- end of message ------
Run the command : DROP FUNCTION IF EXISTS test.func1;
NOTE: This operation completed successfully without error and reported the
following messageMessage reported by PgAdmin4 & OmniDB:
---- start of message ------
function admq.test1() does not exist, skipping
---- end of message ------
-----------------------------------------------------------------------------------------------------------
Proposed solution:
The operation in Step 2 should have failed with the same error as reported
in Step 1;
It's not really that clear to me that doing that would be any more
correct than the alternative. If we changed the behaviour of this then
someone might equally come along later and complain that they
specified "IF EXISTS" and got an error. Maintaining the status quo at
least has the benefit of not randomly changing the behaviour because
it didn't suit one particular use case. The patch to change the
behaviour is pretty trivial and amounts to removing a single line of
code:
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 4661fc4f62..a9912b0986 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2053,12 +2053,11 @@ LookupFuncName(List *funcname, int nargs,
const Oid *argtypes, bool noError)
{
if (clist->next)
{
- if (!noError)
- ereport(ERROR,
-
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
-
errmsg("function name \"%s\" is not unique",
-
NameListToString(funcname)),
-
errhint("Specify the argument list to select the function
unambiguously.")));
+ ereport(ERROR,
+
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+
errmsg("function name \"%s\" is not unique",
+
NameListToString(funcname)),
+
errhint("Specify the argument list to select the function
unambiguously.")));
}
else
return clist->oid;
I just don't know if we'll have a better database by removing it.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
I second David's suggestion
A Marath.
________________________________
From: David G. Johnston <david.g.johnston@gmail.com>
Sent: Friday, January 4, 2019 1:10:05 AM
To: David Rowley
Cc: makmarath@hotmail.com; pgsql-bugs@lists.postgresql.org
Subject: BUG #15572: Misleading message reported by "Drop function operation" on DB with functions having same name
On Thursday, January 3, 2019, David Rowley <david.rowley@2ndquadrant.com<mailto:david.rowley@2ndquadrant.com>> wrote:
If we changed the behaviour of this then
someone might equally come along later and complain that they
specified "IF EXISTS" and got an error.
I’m inclined to argue that the docs say you can only use the omitted-args name if it is unique within the schema. Since the second case is using that form in violation of that requirement reporting an error would match the documentation.
IF EXISTS only applies when no functions exist; an error for ambiguity doesn’t violate its promise; and likely even if we didn’t make it an error something else will fail later on.
It is wrong for the drop function if exists command to translate/print the omitted-args form of the name into a function with zero arguments; it should not be looking explicitly for a zero-arg function as it is not the same thing (as emphasized in the docs).
So, I vote for changing this in 12 but leaving prior versions as-is for compatability as the harm doesn’t seem to be enough to risk breakage. Might be worth a doc patch showing the second case for the back branches (Head seems like it would be good as we are fixing the code to match the documentation, IMO).
David J.
I second David J. Suggestion.
To add to the possible list of solutions I also propose another solution and for better consistency between both the operation
Fix the error message reported by the "drop function without IF Exists" and make it similar to the "Drop.. If Exists".
If no parameters are passed by user then let the "DROP FUNCTION" routine only check for a function of that name which has no parameters => "func1()"
Ash
A Marath.
________________________________
From: David G. Johnston <david.g.johnston@gmail.com>
Sent: Friday, January 4, 2019 1:10:05 AM
To: David Rowley
Cc: makmarath@hotmail.com; pgsql-bugs@lists.postgresql.org
Subject: BUG #15572: Misleading message reported by "Drop function operation" on DB with functions having same name
On Thursday, January 3, 2019, David Rowley <david.rowley@2ndquadrant.com<mailto:david.rowley@2ndquadrant.com>> wrote:
If we changed the behaviour of this then
someone might equally come along later and complain that they
specified "IF EXISTS" and got an error.
I’m inclined to argue that the docs say you can only use the omitted-args name if it is unique within the schema. Since the second case is using that form in violation of that requirement reporting an error would match the documentation.
IF EXISTS only applies when no functions exist; an error for ambiguity doesn’t violate its promise; and likely even if we didn’t make it an error something else will fail later on.
It is wrong for the drop function if exists command to translate/print the omitted-args form of the name into a function with zero arguments; it should not be looking explicitly for a zero-arg function as it is not the same thing (as emphasized in the docs).
So, I vote for changing this in 12 but leaving prior versions as-is for compatability as the harm doesn’t seem to be enough to risk breakage. Might be worth a doc patch showing the second case for the back branches (Head seems like it would be good as we are fixing the code to match the documentation, IMO).
David J.
On 2019-Jan-04, David Rowley wrote:
It's not really that clear to me that doing that would be any more
correct than the alternative.
I think it would be. Specifying a function without params works only if
it's unambiguous; if ambiguity is possible, raise an error. On the
other hand, lack of IF EXISTS is supposed to raise an error if the
function doesn't exist; its presence means not the report that
particular error, but it doesn't mean to suppress other errors such as
the ambiguity one.
I'm not sure what's a good way to implement this, however. Maybe the
solution is to have LookupFuncName return InvalidOid when the function
name is ambiguous and let LookupFuncWithArgs report the error
appropriately. I think this behavior is weird:
/*
* 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.
*/
oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids,
(objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Tue, 8 Jan 2019 at 03:54, Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
I'm not sure what's a good way to implement this, however. Maybe the
solution is to have LookupFuncName return InvalidOid when the function
name is ambiguous and let LookupFuncWithArgs report the error
appropriately. I think this behavior is weird:/*
* 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.
*/
oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids,
(objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
Why can't we just remove the !noError check in the location where the
error is raised?
I had a look and I can't see any other callers that pass nargs as -1
and can pass noError as true. The only place I see is through
get_object_address() in RemoveObjects(). There's another possible call
in get_object_address_rv(), but there's only 1 call in the entire
source for that function and it passes missing_ok as false.
I ended up with the attached.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix.patchapplication/octet-stream; name=drop_func_if_not_exists_fix.patchDownload
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 4661fc4f62..070701044b 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2053,12 +2053,15 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
if (clist->next)
{
- if (!noError)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("function name \"%s\" is not unique",
- NameListToString(funcname)),
- errhint("Specify the argument list to select the function unambiguously.")));
+ /*
+ * Raise an error regardless of if noError is set if there are
+ * multiple matching functions.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("function name \"%s\" is not unique",
+ NameListToString(funcname)),
+ errhint("Specify the argument list to select the function unambiguously.")));
}
else
return clist->oid;
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..33f513e436 100644
--- a/src/test/regress/sql/drop_if_exists.sql
+++ b/src/test/regress/sql/drop_if_exists.sql
@@ -153,6 +153,17 @@ 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(*);
David Rowley <david.rowley@2ndquadrant.com> writes:
Why can't we just remove the !noError check in the location where the
error is raised?
I don't like that a bit --- the point of noError is to prevent throwing
errors, and it doesn't seem like it should be LookupFuncName's business
to decide it's smarter than its callers. Maybe we need another flag
argument?
regards, tom lane
On Wed, 9 Jan 2019 at 13:36, Tom Lane <tgl@sss.pgh.pa.us> wrote:
David Rowley <david.rowley@2ndquadrant.com> writes:
Why can't we just remove the !noError check in the location where the
error is raised?I don't like that a bit --- the point of noError is to prevent throwing
errors, and it doesn't seem like it should be LookupFuncName's business
to decide it's smarter than its callers. Maybe we need another flag
argument?
Well, I guess you didn't have backpatching this in mind. The reason I
thought it was okay to hijack that flag was that the ambiguous error
was only raised when the function parameters were not defined. I
chased around and came to the conclusion this only happened during
DROP. Maybe that's a big assumption as it certainly might not help
future callers passing nargs as -1.
I've attached another version with a newly added flag.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix_v2.patchapplication/octet-stream; name=drop_func_if_not_exists_fix_v2.patchDownload
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(*);
On Wed, 16 Jan 2019 at 12:38, David Rowley <david.rowley@2ndquadrant.com> wrote:
I've attached another version with a newly added flag.
I've added this to the March commitfest.
https://commitfest.postgresql.org/22/1982/
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Wed, 16 Jan 2019 at 12:38, David Rowley <david.rowley@2ndquadrant.com> wrote:
I've attached another version with a newly added flag.
Looks like I missed updating a call in pltcl.c. Thanks to the
commitfest bot for noticing.
Updated patch attached.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix_v3.patchapplication/octet-stream; name=drop_func_if_not_exists_fix_v3.patchDownload
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index c84507b5d0..9f2bad2afd 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 adb77d8f69..91c1ea08c5 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -238,7 +238,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 413ce3fcb6..02ed460847 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 7b98e4b910..86a37e18e3 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 c2e9e41c07..7319c3d922 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -106,7 +106,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);
@@ -154,7 +154,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,
@@ -196,7 +197,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,
@@ -261,7 +262,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)
{
@@ -290,7 +292,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
@@ -300,7 +303,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 0b245a613e..d6fcac7bdb 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -667,7 +667,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 8e5eec22b5..7c1c7deb50 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -108,7 +108,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),
@@ -679,7 +679,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 fa7161ef9d..15b256895e 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 c6ce1011e2..e5cc88b245 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -933,7 +933,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 5222231b51..c2c7a9cacb 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/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index bfbf62305c..05a4b2af16 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -616,7 +616,7 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
/* Parse possibly-qualified identifier and look up the function */
namelist = stringToQualifiedNameList(start_proc);
- procOid = LookupFuncName(namelist, 0, fargtypes, false);
+ procOid = LookupFuncName(namelist, 0, fargtypes, false, false);
/* Current user must have permission to call function */
aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
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(*);
On Thu, 7 Feb 2019 at 01:17, David Rowley <david.rowley@2ndquadrant.com> wrote:
Updated patch attached.
Updated patch attached again. This time due to a newly added call to
LookupFuncName() in 1fb57af92.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix_v4.patchapplication/octet-stream; name=drop_func_if_not_exists_fix_v4.patchDownload
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index c84507b5d0..9f2bad2afd 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 adb77d8f69..91c1ea08c5 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -238,7 +238,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 413ce3fcb6..02ed460847 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/functioncmds.c b/src/backend/commands/functioncmds.c
index 4f62e48d98..b7f9b46d4d 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -658,7 +658,7 @@ interpret_func_support(DefElem *defel)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procName, 1, argList, true);
+ procOid = LookupFuncName(procName, 1, argList, false, true);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 7b98e4b910..86a37e18e3 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 59c4e8dfd0..f0d0bc374d 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -106,7 +106,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,
@@ -198,7 +199,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,
@@ -264,7 +265,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)
{
@@ -293,7 +295,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
@@ -303,7 +306,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 0b245a613e..d6fcac7bdb 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -667,7 +667,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 8e5eec22b5..7c1c7deb50 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -108,7 +108,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),
@@ -679,7 +679,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 448926db12..46a1270470 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1700,28 +1700,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))
@@ -1766,14 +1766,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))
{
@@ -1815,14 +1815,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;
@@ -1845,7 +1845,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;
@@ -1868,7 +1868,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),
@@ -1895,7 +1895,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),
@@ -1922,7 +1922,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),
@@ -1998,7 +1998,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,
@@ -2040,7 +2040,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 c6ce1011e2..e5cc88b245 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -933,7 +933,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 5222231b51..c2c7a9cacb 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/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index bfbf62305c..05a4b2af16 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -616,7 +616,7 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
/* Parse possibly-qualified identifier and look up the function */
namelist = stringToQualifiedNameList(start_proc);
- procOid = LookupFuncName(namelist, 0, fargtypes, false);
+ procOid = LookupFuncName(namelist, 0, fargtypes, false, false);
/* Current user must have permission to call function */
aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
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(*);
David Rowley <david.rowley@2ndquadrant.com> writes:
Updated patch attached again. This time due to a newly added call to
LookupFuncName() in 1fb57af92.
Hmm ... I'd not looked at this before, but now that I do, the new API
for LookupFuncName seems mighty confused, or at least confusingly
documented. It's not clear what the combinations of the flags actually
do, or why you'd want to use them.
I wonder whether you'd be better off replacing the two bools with an
enum, or something like that.
regards, tom lane
On Mon, 11 Feb 2019 at 11:39, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Hmm ... I'd not looked at this before, but now that I do, the new API
for LookupFuncName seems mighty confused, or at least confusingly
documented. It's not clear what the combinations of the flags actually
do, or why you'd want to use them.I wonder whether you'd be better off replacing the two bools with an
enum, or something like that.
Okay. Here's a modified patch with the enum.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix_v5.patchapplication/octet-stream; name=drop_func_if_not_exists_fix_v5.patchDownload
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 11ddce2a8b..f92f2f0a25 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -681,7 +681,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid funcid;
- funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false);
+ funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, funcid);
}
break;
@@ -725,7 +726,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid procid;
- procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false);
+ procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, procid);
}
break;
@@ -735,7 +737,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid routid;
- routid = LookupFuncWithArgs(OBJECT_ROUTINE, func, false);
+ routid = LookupFuncWithArgs(OBJECT_ROUTINE, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, routid);
}
break;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index ab3ec7e356..2f6fd892bf 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -926,7 +926,9 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_PROCEDURE:
case OBJECT_ROUTINE:
address.classId = ProcedureRelationId;
- address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), missing_ok);
+ address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object),
+ missing_ok ? FUNCLOOKUP_ERRIFAMBIGUOUS :
+ FUNCLOOKUP_NORMAL);
address.objectSubId = 0;
break;
case OBJECT_OPERATOR:
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index c84507b5d0..db27616f32 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -254,7 +254,8 @@ 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,
+ FUNCLOOKUP_NORMAL);
/* 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..1329c58d27 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, FUNCLOOKUP_NORMAL);
/* 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 adb77d8f69..8e922e6c2a 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -238,7 +238,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, FUNCLOOKUP_NORMAL);
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 413ce3fcb6..9cb4dadfc4 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,
+ FUNCLOOKUP_NORMAL);
/* 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,
+ FUNCLOOKUP_NORMAL);
/* validator's return value is ignored, so we don't check the type */
}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 4f62e48d98..c3a350d6ed 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -658,7 +658,7 @@ interpret_func_support(DefElem *defel)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procName, 1, argList, true);
+ procOid = LookupFuncName(procName, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1251,7 +1251,8 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
rel = table_open(ProcedureRelationId, RowExclusiveLock);
- funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
+ funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func,
+ FUNCLOOKUP_NORMAL);
ObjectAddressSet(address, ProcedureRelationId, funcOid);
@@ -1567,7 +1568,8 @@ CreateCast(CreateCastStmt *stmt)
{
Form_pg_proc procstruct;
- funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func, false);
+ funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func,
+ FUNCLOOKUP_NORMAL);
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tuple))
@@ -1942,7 +1944,8 @@ CreateTransform(CreateTransformStmt *stmt)
*/
if (stmt->fromsql)
{
- fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql, false);
+ fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql,
+ FUNCLOOKUP_NORMAL);
if (!pg_proc_ownercheck(fromsqlfuncid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5d73848a8b..34312e5e72 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -529,7 +529,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
errmsg("invalid function number %d,"
" must be between 1 and %d",
item->number, maxProcNumber)));
- funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
+ funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name,
+ FUNCLOOKUP_NORMAL);
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
/* Caller must own function */
@@ -908,7 +909,8 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
errmsg("invalid function number %d,"
" must be between 1 and %d",
item->number, maxProcNumber)));
- funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
+ funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name,
+ FUNCLOOKUP_NORMAL);
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
/* Caller must own function */
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 7b98e4b910..6c1bc5208e 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -208,7 +208,8 @@ DefineOperator(List *names, List *parameters)
typeId[1] = typeId2;
nargs = 2;
}
- functionOid = LookupFuncName(functionName, nargs, typeId, false);
+ functionOid = LookupFuncName(functionName, nargs, typeId,
+ FUNCLOOKUP_NORMAL);
/*
* We require EXECUTE rights for the function. This isn't strictly
@@ -271,7 +272,8 @@ ValidateRestrictionEstimator(List *restrictionName)
typeId[2] = INTERNALOID; /* args list */
typeId[3] = INT4OID; /* varRelid */
- restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
+ restrictionOid = LookupFuncName(restrictionName, 4, typeId,
+ FUNCLOOKUP_NORMAL);
/* estimators must return float8 */
if (get_func_rettype(restrictionOid) != FLOAT8OID)
@@ -312,12 +314,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, FUNCLOOKUP_NOERROR);
if (!OidIsValid(joinOid))
- joinOid = LookupFuncName(joinName, 4, typeId, true);
+ joinOid = LookupFuncName(joinName, 4, typeId, FUNCLOOKUP_NOERROR);
/* 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, FUNCLOOKUP_NORMAL);
/* 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 59c4e8dfd0..e94ca419be 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -106,7 +106,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
* return type.
*/
funcname = SystemFuncName(pltemplate->tmplhandler);
- handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
+ handlerOid = LookupFuncName(funcname, 0, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (OidIsValid(handlerOid))
{
funcrettype = get_func_rettype(handlerOid);
@@ -155,7 +156,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
{
funcname = SystemFuncName(pltemplate->tmplinline);
funcargtypes[0] = INTERNALOID;
- inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
+ inlineOid = LookupFuncName(funcname, 1, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (!OidIsValid(inlineOid))
{
tmpAddr = ProcedureCreate(pltemplate->tmplinline,
@@ -198,7 +200,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
{
funcname = SystemFuncName(pltemplate->tmplvalidator);
funcargtypes[0] = OIDOID;
- valOid = LookupFuncName(funcname, 1, funcargtypes, true);
+ valOid = LookupFuncName(funcname, 1, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (!OidIsValid(valOid))
{
tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
@@ -264,7 +267,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,
+ FUNCLOOKUP_NORMAL);
funcrettype = get_func_rettype(handlerOid);
if (funcrettype != LANGUAGE_HANDLEROID)
{
@@ -293,7 +297,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
if (stmt->plinline)
{
funcargtypes[0] = INTERNALOID;
- inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
+ inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes,
+ FUNCLOOKUP_NORMAL);
/* return value is ignored, so we don't check the type */
}
else
@@ -303,7 +308,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
if (stmt->plvalidator)
{
funcargtypes[0] = OIDOID;
- valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
+ valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes,
+ FUNCLOOKUP_NORMAL);
/* 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 0b245a613e..68fb839524 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -667,7 +667,8 @@ 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,
+ FUNCLOOKUP_NORMAL);
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 8e5eec22b5..30d3635598 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -108,7 +108,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, FUNCLOOKUP_NORMAL);
if (get_func_rettype(procOid) != retTypeId)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -679,7 +679,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, FUNCLOOKUP_NORMAL);
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 448926db12..fe7a5c1017 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1700,28 +1700,28 @@ findTypeInputFunction(List *procname, Oid typeOid)
*/
argList[0] = CSTRINGOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
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, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
{
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
}
if (OidIsValid(procOid))
@@ -1766,14 +1766,14 @@ findTypeOutputFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
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, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
{
@@ -1815,14 +1815,14 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
@@ -1845,7 +1845,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
@@ -1868,7 +1868,7 @@ findTypeTypmodinFunction(List *procname)
*/
argList[0] = CSTRINGARRAYOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1895,7 +1895,7 @@ findTypeTypmodoutFunction(List *procname)
*/
argList[0] = INT4OID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1922,7 +1922,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1998,7 +1998,7 @@ findRangeCanonicalFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
@@ -2040,7 +2040,7 @@ findRangeSubtypeDiffFunction(List *procname, Oid subtype)
argList[0] = subtype;
argList[1] = subtype;
- procOid = LookupFuncName(procname, 2, argList, true);
+ procOid = LookupFuncName(procname, 2, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index c6ce1011e2..35fc73b493 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -933,7 +933,7 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
*/
funcargtypes[0] = INTERNALOID;
- handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true);
+ handlerOid = LookupFuncName(rts->method, 1, funcargtypes, FUNCLOOKUP_NOERROR);
/* 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 5222231b51..4192ae9b2d 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2031,18 +2031,25 @@ func_signature_string(List *funcname, int nargs,
* If the function name is not schema-qualified, it is sought in the current
* namespace search path.
*
- * If the function is not found, we return InvalidOid if noError is true,
- * else raise an error.
+ * If the function is not found the behavior depends on the value of
+ * 'checktype'. When it's set to FUNCLOOKUP_NORMAL, we report errors for each
+ * of the possible error situations. When set to FUNCLOOKUP_ERRIFAMBIGUOUS,
+ * the only error we raise is for an ambiguous function definition, otherwise
+ * if set to FUNCLOOKUP_NOERROR we don't raise any errors. In each of the
+ * cases we skip reporting an error, we return InvalidOid to indicate to the
+ * caller that the lookup has failed.
*/
Oid
-LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
+LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
+ FuncLookupCheckType checktype)
{
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,
+ (checktype != FUNCLOOKUP_NORMAL));
/*
* If no arguments were specified, the name must yield a unique candidate.
@@ -2053,7 +2060,11 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
if (clist->next)
{
- if (!noError)
+ /*
+ * Raise this error on FUNCLOOKUP_NORMAL and
+ * FUNCLOOKUP_ERRIFAMBIGUOUS
+ */
+ if (checktype != FUNCLOOKUP_NOERROR)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function name \"%s\" is not unique",
@@ -2065,7 +2076,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
}
else
{
- if (!noError)
+ if (checktype == FUNCLOOKUP_NORMAL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not find a function named \"%s\"",
@@ -2080,7 +2091,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
clist = clist->next;
}
- if (!noError)
+ if (checktype == FUNCLOOKUP_NORMAL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
@@ -2102,10 +2113,12 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
* function.
*/
Oid
-LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
+LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
+ FuncLookupCheckType checktype)
{
Oid argoids[FUNC_MAX_ARGS];
int argcount;
+ bool noError = (checktype != FUNCLOOKUP_NORMAL);
int i;
ListCell *args_item;
Oid oid;
@@ -2134,12 +2147,14 @@ 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
+ * When looking for a function or routine, we pass checktype through to
+ * LookupFuncNameand 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,
- (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
+ oid = LookupFuncName(func->objname,
+ func->args_unspecified ? -1 : argcount,
+ argoids,
+ (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? checktype : FUNCLOOKUP_NOERROR);
if (objtype == OBJECT_FUNCTION)
{
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index ff1477e64f..d279183f17 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -30,6 +30,13 @@ typedef enum
FUNCDETAIL_COERCION /* it's a type coercion request */
} FuncDetailCode;
+/* Error checking types for LookupFuncName and LookupFuncWithArgs */
+typedef enum
+{
+ FUNCLOOKUP_NORMAL, /* Full error checking */
+ FUNCLOOKUP_NOERROR, /* Don't raise error for lookup failure */
+ FUNCLOOKUP_ERRIFAMBIGUOUS /* Only raise ambiguous function errors */
+} FuncLookupCheckType;
extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Node *last_srf, FuncCall *fn, bool proc_call,
@@ -63,9 +70,9 @@ 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);
+ FuncLookupCheckType checktype);
extern Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
- bool noError);
+ FuncLookupCheckType checktype);
extern void check_srf_call_placement(ParseState *pstate, Node *last_srf,
int location);
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index bfbf62305c..f075d2589a 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -616,7 +616,7 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
/* Parse possibly-qualified identifier and look up the function */
namelist = stringToQualifiedNameList(start_proc);
- procOid = LookupFuncName(namelist, 0, fargtypes, false);
+ procOid = LookupFuncName(namelist, 0, fargtypes, FUNCLOOKUP_NORMAL);
/* Current user must have permission to call function */
aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
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(*);
On Mon, Feb 11, 2019 at 03:36:17PM +1300, David Rowley wrote:
Okay. Here's a modified patch with the enum.
FWIW, it makes me a bit uneasy to change this function signature in
back-branches if that's the intention as I suspect that it gets used
in extensions.. For HEAD that's fine of course.
--
Michael
Hello,
On 11.02.2019 05:36, David Rowley wrote:
On Mon, 11 Feb 2019 at 11:39, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I wonder whether you'd be better off replacing the two bools with an
enum, or something like that.Okay. Here's a modified patch with the enum.
There is a LookupFuncWithArgs() call within CreateTransform() where
`bool` is passed still:
tosqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->tosql, *false*);
I had a look and I can't see any other callers that pass nargs as -1
and can pass noError as true. The only place I see is through
get_object_address() in RemoveObjects(). There's another possible call
in get_object_address_rv(), but there's only 1 call in the entire
source for that function and it passes missing_ok as false.
If nargs as -1 and noError as true can be passed only within
RemoveObjects() I wonder, could we just end up with a patch which raise
an error at every ambiguity? That is I mean the following patch:
diff --git a/src/backend/parser/parse_func.c
b/src/backend/parser/parse_func.c
index 5222231b51..cce8f49f52 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2053,7 +2053,6 @@ LookupFuncName(List *funcname, int nargs, const
Oid *argtypes, bool noError)
{
if (clist->next)
{
- if (!noError)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function name \"%s\" is not unique",
But I may overlook something of course.
--
Arthur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
On Fri, 15 Feb 2019 at 02:42, Arthur Zakirov <a.zakirov@postgrespro.ru> wrote:
If nargs as -1 and noError as true can be passed only within
RemoveObjects() I wonder, could we just end up with a patch which raise
an error at every ambiguity? That is I mean the following patch:diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 5222231b51..cce8f49f52 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2053,7 +2053,6 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) { if (clist->next) { - if (!noError) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("function name \"%s\" is not unique",But I may overlook something of course.
I had the same thoughts so I did that in the original patch, but see
Tom's comment which starts with "I don't like that a bit"
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Tue, 12 Feb 2019 at 16:09, Michael Paquier <michael@paquier.xyz> wrote:
FWIW, it makes me a bit uneasy to change this function signature in
back-branches if that's the intention as I suspect that it gets used
in extensions.. For HEAD that's fine of course.
I wondered about this too and questioned Tom about it above. There
was no response.
I just assumed Tom didn't think it was worth fiddling with in back-branches.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
David Rowley <david.rowley@2ndquadrant.com> writes:
On Tue, 12 Feb 2019 at 16:09, Michael Paquier <michael@paquier.xyz> wrote:
FWIW, it makes me a bit uneasy to change this function signature in
back-branches if that's the intention as I suspect that it gets used
in extensions.. For HEAD that's fine of course.
I wondered about this too and questioned Tom about it above. There
was no response.
Sorry, I didn't realize you'd asked a question.
I just assumed Tom didn't think it was worth fiddling with in back-branches.
Yeah, exactly. Not only do I not feel a need to change this behavior
in the back branches, but the original patch is *also* an API change,
in that it changes the behavior of what appears to be a well-defined
boolean parameter. The fact that none of the call sites found in
core today would care doesn't change that; you'd still be risking
breaking extensions, and/or future back-patches.
So I think targeting this for HEAD only is fine.
regards, tom lane
On Sun, Feb 17, 2019 at 05:31:43PM -0500, Tom Lane wrote:
So I think targeting this for HEAD only is fine.
OK, thanks for helping me catching up, Tom and David!
--
Michael
On Sun, Feb 17, 2019 at 11:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
David Rowley <david.rowley@2ndquadrant.com> writes:
On Tue, 12 Feb 2019 at 16:09, Michael Paquier <michael@paquier.xyz> wrote:
FWIW, it makes me a bit uneasy to change this function signature in
back-branches if that's the intention as I suspect that it gets used
in extensions.. For HEAD that's fine of course.I wondered about this too and questioned Tom about it above. There
was no response.Sorry, I didn't realize you'd asked a question.
I just assumed Tom didn't think it was worth fiddling with in back-branches.
Yeah, exactly. Not only do I not feel a need to change this behavior
in the back branches, but the original patch is *also* an API change,
in that it changes the behavior of what appears to be a well-defined
boolean parameter. The fact that none of the call sites found in
core today would care doesn't change that; you'd still be risking
breaking extensions, and/or future back-patches.
Extensions calling those functions with old true/false values probably
won't get any warning or error during compile. Is is something we
should worry about or is it enough to keep the same behavior in this
case?
@david: small typo, you removed a space in this chunk
- * LookupFuncName and let it make any error messages. Otherwise, we make
+ * LookupFuncNameand let it make any error messages. Otherwise, we make
Julien Rouhaud <rjuju123@gmail.com> writes:
On Sun, Feb 17, 2019 at 11:31 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Yeah, exactly. Not only do I not feel a need to change this behavior
in the back branches, but the original patch is *also* an API change,
in that it changes the behavior of what appears to be a well-defined
boolean parameter. The fact that none of the call sites found in
core today would care doesn't change that; you'd still be risking
breaking extensions, and/or future back-patches.
Extensions calling those functions with old true/false values probably
won't get any warning or error during compile. Is is something we
should worry about or is it enough to keep the same behavior in this
case?
Yeah, I thought about that. We can avoid such problems by assigning
the enum values such that 0 and 1 correspond to the old behaviors.
I didn't look to see if the proposed patch does it like that right
now, but it should be an easy fix if not.
regards, tom lane
On Tue, Feb 19, 2019 at 5:46 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Julien Rouhaud <rjuju123@gmail.com> writes:
Extensions calling those functions with old true/false values probably
won't get any warning or error during compile. Is is something we
should worry about or is it enough to keep the same behavior in this
case?Yeah, I thought about that. We can avoid such problems by assigning
the enum values such that 0 and 1 correspond to the old behaviors.
I didn't look to see if the proposed patch does it like that right
now, but it should be an easy fix if not.
It does, I was just wondering whether that was a good enough solution.
Thinking more about it, I'm not sure if there's a general policy for
enums, but should we have an AssertArg() in LookupFuncName[WithArgs]
to check that a correct value was passed?
On Wed, 20 Feb 2019 at 05:00, Julien Rouhaud <rjuju123@gmail.com> wrote:
@david: small typo, you removed a space in this chunk
- * LookupFuncName and let it make any error messages. Otherwise, we make + * LookupFuncNameand let it make any error messages. Otherwise, we make
Thanks. Fixed in the attached.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix_v6.patchapplication/octet-stream; name=drop_func_if_not_exists_fix_v6.patchDownload
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 11ddce2a8b..f92f2f0a25 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -681,7 +681,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid funcid;
- funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false);
+ funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, funcid);
}
break;
@@ -725,7 +726,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid procid;
- procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false);
+ procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, procid);
}
break;
@@ -735,7 +737,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid routid;
- routid = LookupFuncWithArgs(OBJECT_ROUTINE, func, false);
+ routid = LookupFuncWithArgs(OBJECT_ROUTINE, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, routid);
}
break;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index ab3ec7e356..2f6fd892bf 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -926,7 +926,9 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_PROCEDURE:
case OBJECT_ROUTINE:
address.classId = ProcedureRelationId;
- address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), missing_ok);
+ address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object),
+ missing_ok ? FUNCLOOKUP_ERRIFAMBIGUOUS :
+ FUNCLOOKUP_NORMAL);
address.objectSubId = 0;
break;
case OBJECT_OPERATOR:
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index c84507b5d0..db27616f32 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -254,7 +254,8 @@ 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,
+ FUNCLOOKUP_NORMAL);
/* 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..1329c58d27 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, FUNCLOOKUP_NORMAL);
/* 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 adb77d8f69..8e922e6c2a 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -238,7 +238,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, FUNCLOOKUP_NORMAL);
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 413ce3fcb6..9cb4dadfc4 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,
+ FUNCLOOKUP_NORMAL);
/* 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,
+ FUNCLOOKUP_NORMAL);
/* validator's return value is ignored, so we don't check the type */
}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 4f62e48d98..c3a350d6ed 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -658,7 +658,7 @@ interpret_func_support(DefElem *defel)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procName, 1, argList, true);
+ procOid = LookupFuncName(procName, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1251,7 +1251,8 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
rel = table_open(ProcedureRelationId, RowExclusiveLock);
- funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
+ funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func,
+ FUNCLOOKUP_NORMAL);
ObjectAddressSet(address, ProcedureRelationId, funcOid);
@@ -1567,7 +1568,8 @@ CreateCast(CreateCastStmt *stmt)
{
Form_pg_proc procstruct;
- funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func, false);
+ funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func,
+ FUNCLOOKUP_NORMAL);
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tuple))
@@ -1942,7 +1944,8 @@ CreateTransform(CreateTransformStmt *stmt)
*/
if (stmt->fromsql)
{
- fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql, false);
+ fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql,
+ FUNCLOOKUP_NORMAL);
if (!pg_proc_ownercheck(fromsqlfuncid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5d73848a8b..34312e5e72 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -529,7 +529,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
errmsg("invalid function number %d,"
" must be between 1 and %d",
item->number, maxProcNumber)));
- funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
+ funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name,
+ FUNCLOOKUP_NORMAL);
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
/* Caller must own function */
@@ -908,7 +909,8 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
errmsg("invalid function number %d,"
" must be between 1 and %d",
item->number, maxProcNumber)));
- funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
+ funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name,
+ FUNCLOOKUP_NORMAL);
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
/* Caller must own function */
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 7b98e4b910..6c1bc5208e 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -208,7 +208,8 @@ DefineOperator(List *names, List *parameters)
typeId[1] = typeId2;
nargs = 2;
}
- functionOid = LookupFuncName(functionName, nargs, typeId, false);
+ functionOid = LookupFuncName(functionName, nargs, typeId,
+ FUNCLOOKUP_NORMAL);
/*
* We require EXECUTE rights for the function. This isn't strictly
@@ -271,7 +272,8 @@ ValidateRestrictionEstimator(List *restrictionName)
typeId[2] = INTERNALOID; /* args list */
typeId[3] = INT4OID; /* varRelid */
- restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
+ restrictionOid = LookupFuncName(restrictionName, 4, typeId,
+ FUNCLOOKUP_NORMAL);
/* estimators must return float8 */
if (get_func_rettype(restrictionOid) != FLOAT8OID)
@@ -312,12 +314,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, FUNCLOOKUP_NOERROR);
if (!OidIsValid(joinOid))
- joinOid = LookupFuncName(joinName, 4, typeId, true);
+ joinOid = LookupFuncName(joinName, 4, typeId, FUNCLOOKUP_NOERROR);
/* 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, FUNCLOOKUP_NORMAL);
/* 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 59c4e8dfd0..e94ca419be 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -106,7 +106,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
* return type.
*/
funcname = SystemFuncName(pltemplate->tmplhandler);
- handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
+ handlerOid = LookupFuncName(funcname, 0, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (OidIsValid(handlerOid))
{
funcrettype = get_func_rettype(handlerOid);
@@ -155,7 +156,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
{
funcname = SystemFuncName(pltemplate->tmplinline);
funcargtypes[0] = INTERNALOID;
- inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
+ inlineOid = LookupFuncName(funcname, 1, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (!OidIsValid(inlineOid))
{
tmpAddr = ProcedureCreate(pltemplate->tmplinline,
@@ -198,7 +200,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
{
funcname = SystemFuncName(pltemplate->tmplvalidator);
funcargtypes[0] = OIDOID;
- valOid = LookupFuncName(funcname, 1, funcargtypes, true);
+ valOid = LookupFuncName(funcname, 1, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (!OidIsValid(valOid))
{
tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
@@ -264,7 +267,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,
+ FUNCLOOKUP_NORMAL);
funcrettype = get_func_rettype(handlerOid);
if (funcrettype != LANGUAGE_HANDLEROID)
{
@@ -293,7 +297,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
if (stmt->plinline)
{
funcargtypes[0] = INTERNALOID;
- inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
+ inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes,
+ FUNCLOOKUP_NORMAL);
/* return value is ignored, so we don't check the type */
}
else
@@ -303,7 +308,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
if (stmt->plvalidator)
{
funcargtypes[0] = OIDOID;
- valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
+ valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes,
+ FUNCLOOKUP_NORMAL);
/* 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 409bee24f8..60798722a1 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -667,7 +667,8 @@ 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,
+ FUNCLOOKUP_NORMAL);
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 8e5eec22b5..30d3635598 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -108,7 +108,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, FUNCLOOKUP_NORMAL);
if (get_func_rettype(procOid) != retTypeId)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -679,7 +679,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, FUNCLOOKUP_NORMAL);
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 448926db12..fe7a5c1017 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1700,28 +1700,28 @@ findTypeInputFunction(List *procname, Oid typeOid)
*/
argList[0] = CSTRINGOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
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, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
{
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
}
if (OidIsValid(procOid))
@@ -1766,14 +1766,14 @@ findTypeOutputFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
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, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
{
@@ -1815,14 +1815,14 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
@@ -1845,7 +1845,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
@@ -1868,7 +1868,7 @@ findTypeTypmodinFunction(List *procname)
*/
argList[0] = CSTRINGARRAYOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1895,7 +1895,7 @@ findTypeTypmodoutFunction(List *procname)
*/
argList[0] = INT4OID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1922,7 +1922,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1998,7 +1998,7 @@ findRangeCanonicalFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
@@ -2040,7 +2040,7 @@ findRangeSubtypeDiffFunction(List *procname, Oid subtype)
argList[0] = subtype;
argList[1] = subtype;
- procOid = LookupFuncName(procname, 2, argList, true);
+ procOid = LookupFuncName(procname, 2, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index c6ce1011e2..35fc73b493 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -933,7 +933,7 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
*/
funcargtypes[0] = INTERNALOID;
- handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true);
+ handlerOid = LookupFuncName(rts->method, 1, funcargtypes, FUNCLOOKUP_NOERROR);
/* 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 5222231b51..907b2d7062 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2031,18 +2031,25 @@ func_signature_string(List *funcname, int nargs,
* If the function name is not schema-qualified, it is sought in the current
* namespace search path.
*
- * If the function is not found, we return InvalidOid if noError is true,
- * else raise an error.
+ * If the function is not found the behavior depends on the value of
+ * 'checktype'. When it's set to FUNCLOOKUP_NORMAL, we report errors for each
+ * of the possible error situations. When set to FUNCLOOKUP_ERRIFAMBIGUOUS,
+ * the only error we raise is for an ambiguous function definition, otherwise
+ * if set to FUNCLOOKUP_NOERROR we don't raise any errors. In each of the
+ * cases we skip reporting an error, we return InvalidOid to indicate to the
+ * caller that the lookup has failed.
*/
Oid
-LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
+LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
+ FuncLookupCheckType checktype)
{
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,
+ (checktype != FUNCLOOKUP_NORMAL));
/*
* If no arguments were specified, the name must yield a unique candidate.
@@ -2053,7 +2060,11 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
if (clist->next)
{
- if (!noError)
+ /*
+ * Raise this error on FUNCLOOKUP_NORMAL and
+ * FUNCLOOKUP_ERRIFAMBIGUOUS
+ */
+ if (checktype != FUNCLOOKUP_NOERROR)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function name \"%s\" is not unique",
@@ -2065,7 +2076,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
}
else
{
- if (!noError)
+ if (checktype == FUNCLOOKUP_NORMAL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not find a function named \"%s\"",
@@ -2080,7 +2091,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
clist = clist->next;
}
- if (!noError)
+ if (checktype == FUNCLOOKUP_NORMAL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
@@ -2102,10 +2113,12 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
* function.
*/
Oid
-LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
+LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
+ FuncLookupCheckType checktype)
{
Oid argoids[FUNC_MAX_ARGS];
int argcount;
+ bool noError = (checktype != FUNCLOOKUP_NORMAL);
int i;
ListCell *args_item;
Oid oid;
@@ -2134,12 +2147,14 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
}
/*
- * When looking for a function or routine, we pass noError through to
+ * When looking for a function or routine, we pass checktype through to
* LookupFuncName 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,
- (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
+ oid = LookupFuncName(func->objname,
+ func->args_unspecified ? -1 : argcount,
+ argoids,
+ (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? checktype : FUNCLOOKUP_NOERROR);
if (objtype == OBJECT_FUNCTION)
{
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index ff1477e64f..d279183f17 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -30,6 +30,13 @@ typedef enum
FUNCDETAIL_COERCION /* it's a type coercion request */
} FuncDetailCode;
+/* Error checking types for LookupFuncName and LookupFuncWithArgs */
+typedef enum
+{
+ FUNCLOOKUP_NORMAL, /* Full error checking */
+ FUNCLOOKUP_NOERROR, /* Don't raise error for lookup failure */
+ FUNCLOOKUP_ERRIFAMBIGUOUS /* Only raise ambiguous function errors */
+} FuncLookupCheckType;
extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Node *last_srf, FuncCall *fn, bool proc_call,
@@ -63,9 +70,9 @@ 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);
+ FuncLookupCheckType checktype);
extern Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
- bool noError);
+ FuncLookupCheckType checktype);
extern void check_srf_call_placement(ParseState *pstate, Node *last_srf,
int location);
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 76c9afc339..1e651203da 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -616,7 +616,7 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
/* Parse possibly-qualified identifier and look up the function */
namelist = stringToQualifiedNameList(start_proc);
- procOid = LookupFuncName(namelist, 0, fargtypes, false);
+ procOid = LookupFuncName(namelist, 0, fargtypes, FUNCLOOKUP_NORMAL);
/* Current user must have permission to call function */
aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
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(*);
On Wed, 20 Feb 2019 at 06:48, Julien Rouhaud <rjuju123@gmail.com> wrote:
On Tue, Feb 19, 2019 at 5:46 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Julien Rouhaud <rjuju123@gmail.com> writes:
Extensions calling those functions with old true/false values probably
won't get any warning or error during compile. Is is something we
should worry about or is it enough to keep the same behavior in this
case?Yeah, I thought about that. We can avoid such problems by assigning
the enum values such that 0 and 1 correspond to the old behaviors.
I didn't look to see if the proposed patch does it like that right
now, but it should be an easy fix if not.It does, I was just wondering whether that was a good enough solution.
Thinking more about it, I'm not sure if there's a general policy for
enums, but should we have an AssertArg() in LookupFuncName[WithArgs]
to check that a correct value was passed?
I think since the original argument was a bool then it's pretty
unlikely that such an assert would ever catch anything, given 0 and 1
are both valid values for this enum type.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Tue, Feb 19, 2019 at 9:04 PM David Rowley
<david.rowley@2ndquadrant.com> wrote:
On Wed, 20 Feb 2019 at 06:48, Julien Rouhaud <rjuju123@gmail.com> wrote:
On Tue, Feb 19, 2019 at 5:46 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Julien Rouhaud <rjuju123@gmail.com> writes:
Extensions calling those functions with old true/false values probably
won't get any warning or error during compile. Is is something we
should worry about or is it enough to keep the same behavior in this
case?Yeah, I thought about that. We can avoid such problems by assigning
the enum values such that 0 and 1 correspond to the old behaviors.
I didn't look to see if the proposed patch does it like that right
now, but it should be an easy fix if not.It does, I was just wondering whether that was a good enough solution.
Thinking more about it, I'm not sure if there's a general policy for
enums, but should we have an AssertArg() in LookupFuncName[WithArgs]
to check that a correct value was passed?I think since the original argument was a bool then it's pretty
unlikely that such an assert would ever catch anything, given 0 and 1
are both valid values for this enum type.
Indeed. It looks all fine to me in v6, so I'm marking the patch as
ready for committer.
Thanks!
On Wed, 20 Feb 2019 at 09:20, Julien Rouhaud <rjuju123@gmail.com> wrote:
On Tue, Feb 19, 2019 at 9:04 PM David Rowley
<david.rowley@2ndquadrant.com> wrote:I think since the original argument was a bool then it's pretty
unlikely that such an assert would ever catch anything, given 0 and 1
are both valid values for this enum type.Indeed. It looks all fine to me in v6, so I'm marking the patch as
ready for committer.
Great. Thanks for reviewing it.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
On Wed, Feb 20, 2019 at 09:57:19AM +1300, David Rowley wrote:
Great. Thanks for reviewing it.
You forgot to change a call of LookupFuncWithArgs() in
CreateTransform().
- address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), missing_ok);
+ address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object),
+ missing_ok ? FUNCLOOKUP_ERRIFAMBIGUOUS :
+ FUNCLOOKUP_NORMAL);
LookupFuncWithArgs() calls itself LookupFuncName(), which may not use
the check type provided by the caller.. I think that the existing API
is already confusing enough, and this patch makes it a bit more
confusing by adding an extra error layer handling on top of it.
Wouldn't it be more simple from an error handling point of view to
move all the error handling into LookupFuncName() and let the caller
decide what kind of function type handling it expects from the start?
I think that the right call is to add the object type into the
arguments of LookupFuncName().
--
Michael
On Wed, 20 Feb 2019 at 18:56, Michael Paquier <michael@paquier.xyz> wrote:
On Wed, Feb 20, 2019 at 09:57:19AM +1300, David Rowley wrote:
Great. Thanks for reviewing it.
You forgot to change a call of LookupFuncWithArgs() in
CreateTransform().
Yikes, Arthur did mention that, but I somehow managed to stumble over
it when I checked. The attached fixes.
- address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), missing_ok); + address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), + missing_ok ? FUNCLOOKUP_ERRIFAMBIGUOUS : + FUNCLOOKUP_NORMAL);LookupFuncWithArgs() calls itself LookupFuncName(), which may not use
the check type provided by the caller.. I think that the existing API
is already confusing enough, and this patch makes it a bit more
confusing by adding an extra error layer handling on top of it.
Wouldn't it be more simple from an error handling point of view to
move all the error handling into LookupFuncName() and let the caller
decide what kind of function type handling it expects from the start?
I think that the right call is to add the object type into the
arguments of LookupFuncName().
But there are plenty of callers that use LookupFuncName() directly. Do
you happen to know it's okay for all those to error out with the
additional error conditions that such a change would move into that
function? I certainly don't know that.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix_v7.patchapplication/octet-stream; name=drop_func_if_not_exists_fix_v7.patchDownload
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 11ddce2a8b..f92f2f0a25 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -681,7 +681,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid funcid;
- funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false);
+ funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, funcid);
}
break;
@@ -725,7 +726,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid procid;
- procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false);
+ procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, procid);
}
break;
@@ -735,7 +737,8 @@ objectNamesToOids(ObjectType objtype, List *objnames)
ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
Oid routid;
- routid = LookupFuncWithArgs(OBJECT_ROUTINE, func, false);
+ routid = LookupFuncWithArgs(OBJECT_ROUTINE, func,
+ FUNCLOOKUP_NORMAL);
objects = lappend_oid(objects, routid);
}
break;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index ab3ec7e356..2f6fd892bf 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -926,7 +926,9 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_PROCEDURE:
case OBJECT_ROUTINE:
address.classId = ProcedureRelationId;
- address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), missing_ok);
+ address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object),
+ missing_ok ? FUNCLOOKUP_ERRIFAMBIGUOUS :
+ FUNCLOOKUP_NORMAL);
address.objectSubId = 0;
break;
case OBJECT_OPERATOR:
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index c84507b5d0..db27616f32 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -254,7 +254,8 @@ 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,
+ FUNCLOOKUP_NORMAL);
/* 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..1329c58d27 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, FUNCLOOKUP_NORMAL);
/* 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 adb77d8f69..8e922e6c2a 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -238,7 +238,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, FUNCLOOKUP_NORMAL);
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 413ce3fcb6..9cb4dadfc4 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,
+ FUNCLOOKUP_NORMAL);
/* 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,
+ FUNCLOOKUP_NORMAL);
/* validator's return value is ignored, so we don't check the type */
}
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 4f62e48d98..76a4212e05 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -658,7 +658,7 @@ interpret_func_support(DefElem *defel)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procName, 1, argList, true);
+ procOid = LookupFuncName(procName, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1251,7 +1251,8 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
rel = table_open(ProcedureRelationId, RowExclusiveLock);
- funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
+ funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func,
+ FUNCLOOKUP_NORMAL);
ObjectAddressSet(address, ProcedureRelationId, funcOid);
@@ -1567,7 +1568,8 @@ CreateCast(CreateCastStmt *stmt)
{
Form_pg_proc procstruct;
- funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func, false);
+ funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func,
+ FUNCLOOKUP_NORMAL);
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(tuple))
@@ -1942,7 +1944,8 @@ CreateTransform(CreateTransformStmt *stmt)
*/
if (stmt->fromsql)
{
- fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql, false);
+ fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql,
+ FUNCLOOKUP_NORMAL);
if (!pg_proc_ownercheck(fromsqlfuncid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
@@ -1968,7 +1971,8 @@ CreateTransform(CreateTransformStmt *stmt)
if (stmt->tosql)
{
- tosqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->tosql, false);
+ tosqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->tosql,
+ FUNCLOOKUP_NORMAL);
if (!pg_proc_ownercheck(tosqlfuncid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->tosql->objname));
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5d73848a8b..34312e5e72 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -529,7 +529,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
errmsg("invalid function number %d,"
" must be between 1 and %d",
item->number, maxProcNumber)));
- funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
+ funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name,
+ FUNCLOOKUP_NORMAL);
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
/* Caller must own function */
@@ -908,7 +909,8 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
errmsg("invalid function number %d,"
" must be between 1 and %d",
item->number, maxProcNumber)));
- funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
+ funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name,
+ FUNCLOOKUP_NORMAL);
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
/* Caller must own function */
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 7b98e4b910..6c1bc5208e 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -208,7 +208,8 @@ DefineOperator(List *names, List *parameters)
typeId[1] = typeId2;
nargs = 2;
}
- functionOid = LookupFuncName(functionName, nargs, typeId, false);
+ functionOid = LookupFuncName(functionName, nargs, typeId,
+ FUNCLOOKUP_NORMAL);
/*
* We require EXECUTE rights for the function. This isn't strictly
@@ -271,7 +272,8 @@ ValidateRestrictionEstimator(List *restrictionName)
typeId[2] = INTERNALOID; /* args list */
typeId[3] = INT4OID; /* varRelid */
- restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
+ restrictionOid = LookupFuncName(restrictionName, 4, typeId,
+ FUNCLOOKUP_NORMAL);
/* estimators must return float8 */
if (get_func_rettype(restrictionOid) != FLOAT8OID)
@@ -312,12 +314,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, FUNCLOOKUP_NOERROR);
if (!OidIsValid(joinOid))
- joinOid = LookupFuncName(joinName, 4, typeId, true);
+ joinOid = LookupFuncName(joinName, 4, typeId, FUNCLOOKUP_NOERROR);
/* 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, FUNCLOOKUP_NORMAL);
/* 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 59c4e8dfd0..e94ca419be 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -106,7 +106,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
* return type.
*/
funcname = SystemFuncName(pltemplate->tmplhandler);
- handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
+ handlerOid = LookupFuncName(funcname, 0, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (OidIsValid(handlerOid))
{
funcrettype = get_func_rettype(handlerOid);
@@ -155,7 +156,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
{
funcname = SystemFuncName(pltemplate->tmplinline);
funcargtypes[0] = INTERNALOID;
- inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
+ inlineOid = LookupFuncName(funcname, 1, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (!OidIsValid(inlineOid))
{
tmpAddr = ProcedureCreate(pltemplate->tmplinline,
@@ -198,7 +200,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
{
funcname = SystemFuncName(pltemplate->tmplvalidator);
funcargtypes[0] = OIDOID;
- valOid = LookupFuncName(funcname, 1, funcargtypes, true);
+ valOid = LookupFuncName(funcname, 1, funcargtypes,
+ FUNCLOOKUP_NOERROR);
if (!OidIsValid(valOid))
{
tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
@@ -264,7 +267,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,
+ FUNCLOOKUP_NORMAL);
funcrettype = get_func_rettype(handlerOid);
if (funcrettype != LANGUAGE_HANDLEROID)
{
@@ -293,7 +297,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
if (stmt->plinline)
{
funcargtypes[0] = INTERNALOID;
- inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
+ inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes,
+ FUNCLOOKUP_NORMAL);
/* return value is ignored, so we don't check the type */
}
else
@@ -303,7 +308,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
if (stmt->plvalidator)
{
funcargtypes[0] = OIDOID;
- valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
+ valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes,
+ FUNCLOOKUP_NORMAL);
/* 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 409bee24f8..60798722a1 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -667,7 +667,8 @@ 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,
+ FUNCLOOKUP_NORMAL);
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 8e5eec22b5..30d3635598 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -108,7 +108,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, FUNCLOOKUP_NORMAL);
if (get_func_rettype(procOid) != retTypeId)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -679,7 +679,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, FUNCLOOKUP_NORMAL);
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 448926db12..fe7a5c1017 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1700,28 +1700,28 @@ findTypeInputFunction(List *procname, Oid typeOid)
*/
argList[0] = CSTRINGOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
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, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
{
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
}
if (OidIsValid(procOid))
@@ -1766,14 +1766,14 @@ findTypeOutputFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
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, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
{
@@ -1815,14 +1815,14 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
argList[2] = INT4OID;
- procOid = LookupFuncName(procname, 3, argList, true);
+ procOid = LookupFuncName(procname, 3, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
@@ -1845,7 +1845,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (OidIsValid(procOid))
return procOid;
@@ -1868,7 +1868,7 @@ findTypeTypmodinFunction(List *procname)
*/
argList[0] = CSTRINGARRAYOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1895,7 +1895,7 @@ findTypeTypmodoutFunction(List *procname)
*/
argList[0] = INT4OID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1922,7 +1922,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
*/
argList[0] = INTERNALOID;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1998,7 +1998,7 @@ findRangeCanonicalFunction(List *procname, Oid typeOid)
*/
argList[0] = typeOid;
- procOid = LookupFuncName(procname, 1, argList, true);
+ procOid = LookupFuncName(procname, 1, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
@@ -2040,7 +2040,7 @@ findRangeSubtypeDiffFunction(List *procname, Oid subtype)
argList[0] = subtype;
argList[1] = subtype;
- procOid = LookupFuncName(procname, 2, argList, true);
+ procOid = LookupFuncName(procname, 2, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index c6ce1011e2..35fc73b493 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -933,7 +933,7 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
*/
funcargtypes[0] = INTERNALOID;
- handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true);
+ handlerOid = LookupFuncName(rts->method, 1, funcargtypes, FUNCLOOKUP_NOERROR);
/* 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 5222231b51..907b2d7062 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2031,18 +2031,25 @@ func_signature_string(List *funcname, int nargs,
* If the function name is not schema-qualified, it is sought in the current
* namespace search path.
*
- * If the function is not found, we return InvalidOid if noError is true,
- * else raise an error.
+ * If the function is not found the behavior depends on the value of
+ * 'checktype'. When it's set to FUNCLOOKUP_NORMAL, we report errors for each
+ * of the possible error situations. When set to FUNCLOOKUP_ERRIFAMBIGUOUS,
+ * the only error we raise is for an ambiguous function definition, otherwise
+ * if set to FUNCLOOKUP_NOERROR we don't raise any errors. In each of the
+ * cases we skip reporting an error, we return InvalidOid to indicate to the
+ * caller that the lookup has failed.
*/
Oid
-LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
+LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
+ FuncLookupCheckType checktype)
{
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,
+ (checktype != FUNCLOOKUP_NORMAL));
/*
* If no arguments were specified, the name must yield a unique candidate.
@@ -2053,7 +2060,11 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
if (clist->next)
{
- if (!noError)
+ /*
+ * Raise this error on FUNCLOOKUP_NORMAL and
+ * FUNCLOOKUP_ERRIFAMBIGUOUS
+ */
+ if (checktype != FUNCLOOKUP_NOERROR)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function name \"%s\" is not unique",
@@ -2065,7 +2076,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
}
else
{
- if (!noError)
+ if (checktype == FUNCLOOKUP_NORMAL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not find a function named \"%s\"",
@@ -2080,7 +2091,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
clist = clist->next;
}
- if (!noError)
+ if (checktype == FUNCLOOKUP_NORMAL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
@@ -2102,10 +2113,12 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
* function.
*/
Oid
-LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
+LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
+ FuncLookupCheckType checktype)
{
Oid argoids[FUNC_MAX_ARGS];
int argcount;
+ bool noError = (checktype != FUNCLOOKUP_NORMAL);
int i;
ListCell *args_item;
Oid oid;
@@ -2134,12 +2147,14 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
}
/*
- * When looking for a function or routine, we pass noError through to
+ * When looking for a function or routine, we pass checktype through to
* LookupFuncName 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,
- (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
+ oid = LookupFuncName(func->objname,
+ func->args_unspecified ? -1 : argcount,
+ argoids,
+ (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? checktype : FUNCLOOKUP_NOERROR);
if (objtype == OBJECT_FUNCTION)
{
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index ff1477e64f..d279183f17 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -30,6 +30,13 @@ typedef enum
FUNCDETAIL_COERCION /* it's a type coercion request */
} FuncDetailCode;
+/* Error checking types for LookupFuncName and LookupFuncWithArgs */
+typedef enum
+{
+ FUNCLOOKUP_NORMAL, /* Full error checking */
+ FUNCLOOKUP_NOERROR, /* Don't raise error for lookup failure */
+ FUNCLOOKUP_ERRIFAMBIGUOUS /* Only raise ambiguous function errors */
+} FuncLookupCheckType;
extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Node *last_srf, FuncCall *fn, bool proc_call,
@@ -63,9 +70,9 @@ 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);
+ FuncLookupCheckType checktype);
extern Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
- bool noError);
+ FuncLookupCheckType checktype);
extern void check_srf_call_placement(ParseState *pstate, Node *last_srf,
int location);
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 76c9afc339..1e651203da 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -616,7 +616,7 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
/* Parse possibly-qualified identifier and look up the function */
namelist = stringToQualifiedNameList(start_proc);
- procOid = LookupFuncName(namelist, 0, fargtypes, false);
+ procOid = LookupFuncName(namelist, 0, fargtypes, FUNCLOOKUP_NORMAL);
/* Current user must have permission to call function */
aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
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(*);
David Rowley <david.rowley@2ndquadrant.com> writes:
On Wed, 20 Feb 2019 at 18:56, Michael Paquier <michael@paquier.xyz> wrote:
I think that the right call is to add the object type into the
arguments of LookupFuncName().
I'm not clear how that helps exactly?
But there are plenty of callers that use LookupFuncName() directly. Do
you happen to know it's okay for all those to error out with the
additional error conditions that such a change would move into that
function? I certainly don't know that.
The real problem here is that you've unilaterally decided that all callers
of get_object_address() need a particular behavior --- and not only that,
but a behavior that seems fairly surprising and unprincipled, given that
get_object_address's option is documented as "missing_ok" (something the
patch doesn't even bother to change). It's not very apparent to me why
function-related lookups should start behaving differently from other
lookups in that function, and it's sure not apparent that all callers of
get_object_address() are on board with it.
Should we be propagating that 3-way flag further up, to
get_object_address() callers? I dunno.
regards, tom lane
On Thu, 21 Feb 2019 at 09:36, Tom Lane <tgl@sss.pgh.pa.us> wrote:
David Rowley <david.rowley@2ndquadrant.com> writes:
On Wed, 20 Feb 2019 at 18:56, Michael Paquier <michael@paquier.xyz> wrote:
I think that the right call is to add the object type into the
arguments of LookupFuncName().I'm not clear how that helps exactly?
But there are plenty of callers that use LookupFuncName() directly. Do
you happen to know it's okay for all those to error out with the
additional error conditions that such a change would move into that
function? I certainly don't know that.The real problem here is that you've unilaterally decided that all callers
of get_object_address() need a particular behavior --- and not only that,
but a behavior that seems fairly surprising and unprincipled, given that
get_object_address's option is documented as "missing_ok" (something the
patch doesn't even bother to change). It's not very apparent to me why
function-related lookups should start behaving differently from other
lookups in that function, and it's sure not apparent that all callers of
get_object_address() are on board with it.
I assume you're talking about:
* If the object is not found, an error is thrown, unless missing_ok is
* true. In this case, no lock is acquired, relp is set to NULL, and the
* returned address has objectId set to InvalidOid.
Well, I didn't update that comment because the code I've changed does
nothing different for the missing_ok case. The missing function error
is still raised or not raised correctly depending on the value of that
flag.
I understand your original gripe with the patch where I had changed
the meaning of noError to mean
"noError-Apart-From-If-Its-An-Ambiguous-Function", without much of any
documentation to mention that fact, but it seems to me that this time
around your confusing missing_ok with noError. To me noError means
don't raise an error, and missing_ok is intended for use with IF [NOT]
EXISTS... Yes, it might be getting used for something else, but since
we still raise an error when the function is missing when the flag is
set to false and don't when it's set to true. I fail to see why that
breaks the contract that's documented in the above comment. If you
think it does then please explain why.
Should we be propagating that 3-way flag further up, to
get_object_address() callers? I dunno.
I don't see why that's needed given what's mentioned above.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
David Rowley <david.rowley@2ndquadrant.com> writes:
On Thu, 21 Feb 2019 at 09:36, Tom Lane <tgl@sss.pgh.pa.us> wrote:
The real problem here is that you've unilaterally decided that all callers
of get_object_address() need a particular behavior --- and not only that,
but a behavior that seems fairly surprising and unprincipled, given that
get_object_address's option is documented as "missing_ok" (something the
patch doesn't even bother to change).
...
Should we be propagating that 3-way flag further up, to
get_object_address() callers? I dunno.
I don't see why that's needed given what's mentioned above.
Well, if we're not going to propagate the option further up, then I think
we might as well do it like you originally suggested, ie leave the
signature of LookupFuncName alone and just document that the
ambiguous-function case is not covered by noError. As far as I can tell,
just about all the other callers besides get_object_address() have no
interest in this issue because they're not passing nargs == -1.
What's more, a lot of them look like this example in
findRangeSubtypeDiffFunction:
procOid = LookupFuncName(procname, 2, argList, FUNCLOOKUP_NOERROR);
if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procname, 2, NIL, argList))));
so that if some day in the future FUNCLOOKUP_NOERROR could actually
suppress an ambiguous-function error here, the caller would proceed
to report an incorrect/misleading error message. It doesn't seem to
make much sense to allow callers to separately suppress or not
suppress ambiguous-function unless we also change the return
convention so that the callers can tell which case happened.
And that's looking a bit pointless, at least for now.
So, sorry for making you chase down this dead end, but it wasn't
obvious until now (to me anyway) that it was a dead end.
I did notice though that the patch fails to cover the same problem
right next door for procedures:
regression=# create procedure funcp(param1 text) language sql as 'select $1';
CREATE PROCEDURE
regression=# create procedure funcp(param1 int) language sql as 'select $1';
CREATE PROCEDURE
regression=# drop procedure funcp;
ERROR: could not find a procedure named "funcp"
This should surely be complaining about ambiguity, rather than giving
the same error text as if there were zero matches.
Possibly the same occurs for aggregates, though I'm not sure if that
code is reachable --- DROP AGGREGATE, at least, won't let you omit the
arguments.
I think the underlying cause of this is that LookupFuncWithArgs is in
the same situation I just complained for outside callers: it cannot tell
whether its noError request suppressed a not-found or ambiguous-function
case. Maybe the way to proceed here is to refactor within parse_func.c
so that there's an underlying function that returns an indicator of what
happened, and both LookupFuncName and LookupFuncWithArgs call it, each
with their own ideas about how to phrase the error reports. It's
certainly mighty ugly that LookupFuncWithArgs farms out the actual
error report in some cases and not others.
regards, tom lane
On Mon, 4 Mar 2019 at 09:14, Tom Lane <tgl@sss.pgh.pa.us> wrote:
David Rowley <david.rowley@2ndquadrant.com> writes:
On Thu, 21 Feb 2019 at 09:36, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Should we be propagating that 3-way flag further up, to
get_object_address() callers? I dunno.I don't see why that's needed given what's mentioned above.
Well, if we're not going to propagate the option further up, then I think
we might as well do it like you originally suggested, ie leave the
signature of LookupFuncName alone and just document that the
ambiguous-function case is not covered by noError. As far as I can tell,
just about all the other callers besides get_object_address() have no
interest in this issue because they're not passing nargs == -1.
Okay.
I think the underlying cause of this is that LookupFuncWithArgs is in
the same situation I just complained for outside callers: it cannot tell
whether its noError request suppressed a not-found or ambiguous-function
case. Maybe the way to proceed here is to refactor within parse_func.c
so that there's an underlying function that returns an indicator of what
happened, and both LookupFuncName and LookupFuncWithArgs call it, each
with their own ideas about how to phrase the error reports. It's
certainly mighty ugly that LookupFuncWithArgs farms out the actual
error report in some cases and not others.
I started working on this, but... damage control... I don't want to
take it too far without you having a glance at it first.
I've invented a new function by the name of LookupFuncNameInternal().
This attempts to find the function, but if it can't or the name is
ambiguous then it returns InvalidOid and sets an error code parameter.
I've made both LookupFuncName and LookupFuncWithArgs use this.
In my travels, I discovered something else that does not seem very great.
postgres=# create procedure abc(int) as $$ begin end; $$ language plpgsql;
CREATE PROCEDURE
postgres=# drop function if exists abc(int);
NOTICE: function abc(pg_catalog.int4) does not exist, skipping
DROP FUNCTION
I think it would be better to ERROR in that case. So with the attached
we now get:
postgres=# create procedure abc(int) as $$ begin end; $$ language plpgsql;
CREATE PROCEDURE
postgres=# drop function if exists abc(int);
ERROR: abc(integer) is not a function
I've also tried to have the error messages mention procedure when the
object is a procedure and function when its a function. It looks like
the previous code was calling LookupFuncName() with noError=true so it
could handle using "procedure" in the error messages itself, but it
failed to do that for an ambiguous procedure name. That should now be
fixed.
I also touched the too many function arguments case, but perhaps I
need to go further there and do something for aggregates. I've not
thought too hard about that.
I've not really read the patch back or done any polishing yet. Manual
testing done is minimal, and I didn't add tests for the new behaviour
change either. I can do more if the feedback is positive.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix_v8.patchapplication/octet-stream; name=drop_func_if_not_exists_fix_v8.patchDownload
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 654ee80b27..86b0d9fba7 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -34,6 +34,11 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+typedef enum
+{
+ FUNCLOOKUP_NOSUCHFUNC,
+ FUNCLOOKUP_AMBIGUOUS
+} FuncLookupError;
static void unify_hypothetical_args(ParseState *pstate,
List *fargs, int numAggregatedArgs,
@@ -41,6 +46,9 @@ static void unify_hypothetical_args(ParseState *pstate,
static Oid FuncNameAsType(List *funcname);
static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
Node *first_arg, int location);
+static Oid LookupFuncNameInternal(List *funcname, int nargs,
+ const Oid *argtypes,
+ bool noError, FuncLookupError *lookupError);
/*
@@ -2022,20 +2030,19 @@ func_signature_string(List *funcname, int nargs,
}
/*
- * LookupFuncName
- *
- * Given a possibly-qualified function name and optionally a set of argument
- * types, look up the function. Pass nargs == -1 to indicate that no argument
- * types are specified.
+ * LookupFuncNameInternal
+ * Lookup a function Oid from its name and arguments.
*
- * If the function name is not schema-qualified, it is sought in the current
- * namespace search path.
+ * In an error situation, e.g. can't find the function then we return
+ * InvalidOid and set *lookupError to indicate what went wrong.
*
- * If the function is not found, we return InvalidOid if noError is true,
- * else raise an error.
+ * Possible Errors:
+ * FUNCLOOKUP_NOSUCHFUNC -- When we can't find a function of this name.
+ * FUNCLOOKUP_AMBIGUOUS -- When nargs == -1 and more than one func matches.
*/
-Oid
-LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
+static Oid
+LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes,
+ bool noError, FuncLookupError *lookupError)
{
FuncCandidateList clist;
@@ -2051,25 +2058,20 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
if (clist)
{
+ /* If there is another match then it's an ambiguous error */
if (clist->next)
{
- if (!noError)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("function name \"%s\" is not unique",
- NameListToString(funcname)),
- errhint("Specify the argument list to select the function unambiguously.")));
+ *lookupError = FUNCLOOKUP_AMBIGUOUS;
+ return InvalidOid;
}
+ /* Otherwise return the match */
else
return clist->oid;
}
else
{
- if (!noError)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not find a function named \"%s\"",
- NameListToString(funcname))));
+ *lookupError = FUNCLOOKUP_NOSUCHFUNC;
+ return InvalidOid;
}
}
@@ -2080,16 +2082,75 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
clist = clist->next;
}
- if (!noError)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("function %s does not exist",
- func_signature_string(funcname, nargs,
- NIL, argtypes))));
-
+ *lookupError = FUNCLOOKUP_NOSUCHFUNC;
return InvalidOid;
}
+/*
+ * LookupFuncName
+ *
+ * Given a possibly-qualified function name and optionally a set of argument
+ * types, look up the function. Pass nargs == -1 to indicate that no argument
+ * types are specified.
+ *
+ * If the function name is not schema-qualified, it is sought in the current
+ * namespace search path.
+ *
+ * If the function is not found, we return InvalidOid if noError is true,
+ * else raise an error.
+ *
+ * If nargs == -1 and multiple functions are found matching this function name
+ * then we raise an ambiguous function error regardless of what noError is set
+ * to.
+ */
+Oid
+LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
+{
+ FuncLookupError lookupError;
+ Oid funcoid;
+
+ funcoid = LookupFuncNameInternal(funcname, nargs, argtypes, noError,
+ &lookupError);
+
+ if (OidIsValid(funcoid))
+ return funcoid;
+
+ switch (lookupError)
+ {
+ case FUNCLOOKUP_AMBIGUOUS:
+ /*
+ * Raise an error regardless of if noError is set if there are
+ * multiple matching functions.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("function name \"%s\" is not unique",
+ NameListToString(funcname)),
+ errhint("Specify the argument list to select the function unambiguously.")));
+ break;
+
+ case FUNCLOOKUP_NOSUCHFUNC:
+ /* Let the caller deal with it when noError is true */
+ if (noError)
+ return InvalidOid;
+
+ if (nargs == -1)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find a function named \"%s\"",
+ NameListToString(funcname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(funcname, nargs,
+ NIL, argtypes))));
+ break;
+ }
+
+ return InvalidOid; /* Keep compiler quiet */
+}
+
/*
* LookupFuncWithArgs
*
@@ -2104,8 +2165,10 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
Oid
LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
{
+ FuncLookupError lookupError;
Oid argoids[FUNC_MAX_ARGS];
int argcount;
+ int nargs;
int i;
ListCell *args_item;
Oid oid;
@@ -2117,12 +2180,22 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
argcount = list_length(func->objargs);
if (argcount > FUNC_MAX_ARGS)
- ereport(ERROR,
+ {
+ if (objtype == OBJECT_PROCEDURE)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+ errmsg_plural("procedure cannot have more than %d argument",
+ "procedure cannot have more than %d arguments",
+ FUNC_MAX_ARGS,
+ FUNC_MAX_ARGS)));
+ else
+ ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg_plural("functions cannot have more than %d argument",
"functions cannot have more than %d arguments",
FUNC_MAX_ARGS,
FUNC_MAX_ARGS)));
+ }
i = 0;
foreach(args_item, func->objargs)
@@ -2133,97 +2206,156 @@ 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.
+ * Set nargs for the LookupFuncName call. It expects -1 to mean no args
+ * were specified.
*/
- oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids,
- (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
+ nargs = func->args_unspecified ? -1 : argcount;
- if (objtype == OBJECT_FUNCTION)
- {
- /* Make sure it's a function, not a procedure */
- if (oid && get_func_prokind(oid) == PROKIND_PROCEDURE)
- {
- if (noError)
- return InvalidOid;
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("%s is not a function",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
- }
- }
- else if (objtype == OBJECT_PROCEDURE)
+ oid = LookupFuncNameInternal(func->objname, nargs, argoids, noError,
+ &lookupError);
+
+ if (OidIsValid(oid))
{
- if (!OidIsValid(oid))
+ /*
+ * Even if we found the function, perform validation that the objtype
+ * matches the prokind of the found function. For historical reasons
+ * we allow the objtype of FUNCTION to mean other things, but we draw
+ * the line if the object is a procedure. This is a new enough
+ * feature that this historical rule does not apply.
+ */
+ switch (objtype)
{
- if (noError)
- return InvalidOid;
- else if (func->args_unspecified)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not find a procedure named \"%s\"",
- NameListToString(func->objname))));
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("procedure %s does not exist",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
- }
+ case OBJECT_FUNCTION:
+ /* Only complain if it's a procedure. */
+ if (get_func_prokind(oid) == PROKIND_PROCEDURE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s is not a function",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
- /* Make sure it's a procedure */
- if (get_func_prokind(oid) != PROKIND_PROCEDURE)
- {
- if (noError)
- return InvalidOid;
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("%s is not a procedure",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
+ case OBJECT_PROCEDURE:
+ /* Reject found object is not a procedure */
+ if (get_func_prokind(oid) != PROKIND_PROCEDURE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s is not a procedure",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+
+ case OBJECT_AGGREGATE:
+ /* Reject found object is not an aggregate */
+ if (get_func_prokind(oid) != PROKIND_AGGREGATE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s is not an aggregate",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+
+ default:
+ /* Don't complain for routines */
+ break;
}
+
+ return oid; /* All good */
}
- else if (objtype == OBJECT_AGGREGATE)
+
+ /* Deal with cases where the lookup failed */
+ else
{
- if (!OidIsValid(oid))
+ switch (lookupError)
{
- if (noError)
+ case FUNCLOOKUP_AMBIGUOUS:
+ switch (objtype)
+ {
+ case OBJECT_FUNCTION:
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("function name \"%s\" is not unique",
+ NameListToString(func->objname)),
+ errhint("Specify the argument list to select the function unambiguously.")));
+ break;
+ case OBJECT_PROCEDURE:
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("procedure name \"%s\" is not unique",
+ NameListToString(func->objname)),
+ errhint("Specify the argument list to select the procedure unambiguously.")));
+ break;
+ case OBJECT_AGGREGATE:
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("aggregate name \"%s\" is not unique",
+ NameListToString(func->objname)),
+ errhint("Specify the argument list to select the aggregate unambiguously.")));
+ break;
+ default:
+ break;
+ }
return InvalidOid;
- else if (func->args_unspecified)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not find an aggregate named \"%s\"",
- NameListToString(func->objname))));
- else if (argcount == 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("aggregate %s(*) does not exist",
- NameListToString(func->objname))));
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("aggregate %s does not exist",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
- }
- /* Make sure it's an aggregate */
- if (get_func_prokind(oid) != PROKIND_AGGREGATE)
- {
- if (noError)
- return InvalidOid;
- /* we do not use the (*) notation for functions... */
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("function %s is not an aggregate",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
+ case FUNCLOOKUP_NOSUCHFUNC:
+
+ /* Suppress no such func errors when noError is true */
+ if (noError)
+ break;
+
+ switch (objtype)
+ {
+ case OBJECT_PROCEDURE:
+ if (func->args_unspecified)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find a procedure named \"%s\"",
+ NameListToString(func->objname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("procedure %s does not exist",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+
+ case OBJECT_AGGREGATE:
+ if (func->args_unspecified)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find an aggregate named \"%s\"",
+ NameListToString(func->objname))));
+ else if (argcount == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("aggregate %s(*) does not exist",
+ NameListToString(func->objname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("aggregate %s does not exist",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+
+ default:
+ /* FUNCTION and ROUTINE */
+ if (func->args_unspecified)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find a function named \"%s\"",
+ NameListToString(func->objname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+ }
}
+ return InvalidOid;
}
-
- return oid;
}
/*
diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out
index b80c5ed2d8..5290b1ca7d 100644
--- a/src/test/regress/expected/drop_if_exists.out
+++ b/src/test/regress/expected/drop_if_exists.out
@@ -138,6 +138,27 @@ 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);
+-- Ensure we get an ambiguous function error for procedures too.
+CREATE PROCEDURE test_ambiguous_procname(int) as $$ begin end; $$ language plpgsql;
+CREATE PROCEDURE test_ambiguous_procname(text) as $$ begin end; $$ language plpgsql;
+DROP PROCEDURE IF EXISTS test_ambiguous_procname;
+ERROR: procedure name "test_ambiguous_procname" is not unique
+HINT: Specify the argument list to select the procedure unambiguously.
+-- cleanup
+DROP FUNCTION test_ambiguous_procname(int);
+ERROR: test_ambiguous_procname(integer) is not a function
+DROP FUNCTION test_ambiguous_procname(text);
+ERROR: test_ambiguous_procname(text) is not a function
-- 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..138a713b9e 100644
--- a/src/test/regress/sql/drop_if_exists.sql
+++ b/src/test/regress/sql/drop_if_exists.sql
@@ -153,6 +153,25 @@ 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);
+
+-- Ensure we get an ambiguous function error for procedures too.
+CREATE PROCEDURE test_ambiguous_procname(int) as $$ begin end; $$ language plpgsql;
+CREATE PROCEDURE test_ambiguous_procname(text) as $$ begin end; $$ language plpgsql;
+DROP PROCEDURE IF EXISTS test_ambiguous_procname;
+
+-- cleanup
+DROP FUNCTION test_ambiguous_procname(int);
+DROP FUNCTION test_ambiguous_procname(text);
+
-- aggregate
DROP AGGREGATE test_aggregate_exists(*);
DROP AGGREGATE IF EXISTS test_aggregate_exists(*);
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: not tested
Documentation: not tested
I read a discussion and I think so currently implemented behave (by last patch) is correct in all details.
I propose maybe more strongly comment fact so noError is applied only on "not found" event. In other cases, this flag is ignored and error is raised immediately there. I think so it is not good enough commented why.
This is significant change - in previous releases, noError was used like really noError, so should be commented more.
Regress tests are enough.
The patch is possible to apply without problems and compile without warnings
The new status of this patch is: Ready for Committer
Thanks for reviewing this.
On Wed, 20 Mar 2019 at 04:31, Pavel Stehule <pavel.stehule@gmail.com> wrote:
I propose maybe more strongly comment fact so noError is applied only on "not found" event. In other cases, this flag is ignored and error is raised immediately there. I think so it is not good enough commented why.
This is significant change - in previous releases, noError was used like really noError, so should be commented more.
I've made a change to the comments in LookupFuncWithArgs() to make
this more clear. I also ended up renaming noError to missing_ok.
Hopefully this backs up the comments and reduces the chances of
surprises.
Regress tests are enough.
The patch is possible to apply without problems and compile without warnings
Thanks. I also fixed a bug that caused an Assert failure when
performing DROP ROUTINE ambiguous_name; test added for that case too.
The new status of this patch is: Ready for Committer
Great!
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
drop_func_if_not_exists_fix_v9.patchapplication/octet-stream; name=drop_func_if_not_exists_fix_v9.patchDownload
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 654ee80b27..6899a9f033 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -34,6 +34,14 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+/*
+ * Error codes for when function lookup failures occur
+ */
+typedef enum
+{
+ FUNCLOOKUP_NOSUCHFUNC,
+ FUNCLOOKUP_AMBIGUOUS
+} FuncLookupError;
static void unify_hypothetical_args(ParseState *pstate,
List *fargs, int numAggregatedArgs,
@@ -41,6 +49,9 @@ static void unify_hypothetical_args(ParseState *pstate,
static Oid FuncNameAsType(List *funcname);
static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
Node *first_arg, int location);
+static Oid LookupFuncNameInternal(List *funcname, int nargs,
+ const Oid *argtypes,
+ bool missing_ok, FuncLookupError *lookupError);
/*
@@ -2022,27 +2033,27 @@ func_signature_string(List *funcname, int nargs,
}
/*
- * LookupFuncName
- *
- * Given a possibly-qualified function name and optionally a set of argument
- * types, look up the function. Pass nargs == -1 to indicate that no argument
- * types are specified.
+ * LookupFuncNameInternal
+ * Lookup a function Oid from its name and arguments.
*
- * If the function name is not schema-qualified, it is sought in the current
- * namespace search path.
+ * In an error situation, e.g. can't find the function, then we return
+ * InvalidOid and set *lookupError to indicate what went wrong.
*
- * If the function is not found, we return InvalidOid if noError is true,
- * else raise an error.
+ * Possible Errors:
+ * FUNCLOOKUP_NOSUCHFUNC -- When we can't find a function of this name.
+ * FUNCLOOKUP_AMBIGUOUS -- When nargs == -1 and more than one func matches.
*/
-Oid
-LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
+static Oid
+LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes,
+ bool missing_ok, FuncLookupError *lookupError)
{
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,
+ missing_ok);
/*
* If no arguments were specified, the name must yield a unique candidate.
@@ -2051,25 +2062,20 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
{
if (clist)
{
+ /* If there is another match then it's an ambiguous error */
if (clist->next)
{
- if (!noError)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("function name \"%s\" is not unique",
- NameListToString(funcname)),
- errhint("Specify the argument list to select the function unambiguously.")));
+ *lookupError = FUNCLOOKUP_AMBIGUOUS;
+ return InvalidOid;
}
+ /* Otherwise return the match */
else
return clist->oid;
}
else
{
- if (!noError)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not find a function named \"%s\"",
- NameListToString(funcname))));
+ *lookupError = FUNCLOOKUP_NOSUCHFUNC;
+ return InvalidOid;
}
}
@@ -2080,16 +2086,75 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
clist = clist->next;
}
- if (!noError)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("function %s does not exist",
- func_signature_string(funcname, nargs,
- NIL, argtypes))));
-
+ *lookupError = FUNCLOOKUP_NOSUCHFUNC;
return InvalidOid;
}
+/*
+ * LookupFuncName
+ *
+ * Given a possibly-qualified function name and optionally a set of argument
+ * types, look up the function. Pass nargs == -1 to indicate that no argument
+ * types are specified.
+ *
+ * If the function name is not schema-qualified, it is sought in the current
+ * namespace search path.
+ *
+ * If the function is not found, we return InvalidOid if missing_ok is true,
+ * else raise an error.
+ *
+ * If nargs == -1 and multiple functions are found matching this function name
+ * then we raise an ambiguous function error regardless of what missing_ok is
+ * set to.
+ */
+Oid
+LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
+{
+ FuncLookupError lookupError;
+ Oid funcoid;
+
+ funcoid = LookupFuncNameInternal(funcname, nargs, argtypes, missing_ok,
+ &lookupError);
+
+ if (OidIsValid(funcoid))
+ return funcoid;
+
+ switch (lookupError)
+ {
+ case FUNCLOOKUP_AMBIGUOUS:
+ /*
+ * Raise an error regardless of if missing_ok is set if there are
+ * multiple matching functions.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("function name \"%s\" is not unique",
+ NameListToString(funcname)),
+ errhint("Specify the argument list to select the function unambiguously.")));
+ break;
+
+ case FUNCLOOKUP_NOSUCHFUNC:
+ /* Let the caller deal with it when missing_ok is true */
+ if (missing_ok)
+ return InvalidOid;
+
+ if (nargs == -1)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find a function named \"%s\"",
+ NameListToString(funcname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(funcname, nargs,
+ NIL, argtypes))));
+ break;
+ }
+
+ return InvalidOid; /* Keep compiler quiet */
+}
+
/*
* LookupFuncWithArgs
*
@@ -2100,12 +2165,18 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
*
* For historical reasons, we also accept aggregates when looking for a
* function.
+ *
+ * When missing_ok is true we don't generate any error for missing objects and
+ * return InvalidOid. Other types of errors can still be raised, regardless
+ * of the value of missing_ok.
*/
Oid
-LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
+LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok)
{
+ FuncLookupError lookupError;
Oid argoids[FUNC_MAX_ARGS];
int argcount;
+ int nargs;
int i;
ListCell *args_item;
Oid oid;
@@ -2117,113 +2188,191 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
argcount = list_length(func->objargs);
if (argcount > FUNC_MAX_ARGS)
- ereport(ERROR,
+ {
+ if (objtype == OBJECT_PROCEDURE)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+ errmsg_plural("procedure cannot have more than %d argument",
+ "procedure cannot have more than %d arguments",
+ FUNC_MAX_ARGS,
+ FUNC_MAX_ARGS)));
+ else
+ ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg_plural("functions cannot have more than %d argument",
"functions cannot have more than %d arguments",
FUNC_MAX_ARGS,
FUNC_MAX_ARGS)));
+ }
i = 0;
foreach(args_item, func->objargs)
{
TypeName *t = (TypeName *) lfirst(args_item);
- argoids[i++] = LookupTypeNameOid(NULL, t, noError);
+ argoids[i++] = LookupTypeNameOid(NULL, t, missing_ok);
}
/*
- * 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.
+ * Set nargs for the LookupFuncName call. It expects -1 to mean no args
+ * were specified.
*/
- oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids,
- (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
+ nargs = func->args_unspecified ? -1 : argcount;
- if (objtype == OBJECT_FUNCTION)
- {
- /* Make sure it's a function, not a procedure */
- if (oid && get_func_prokind(oid) == PROKIND_PROCEDURE)
- {
- if (noError)
- return InvalidOid;
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("%s is not a function",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
- }
- }
- else if (objtype == OBJECT_PROCEDURE)
+ oid = LookupFuncNameInternal(func->objname, nargs, argoids, missing_ok,
+ &lookupError);
+
+ if (OidIsValid(oid))
{
- if (!OidIsValid(oid))
+ /*
+ * Even if we found the function, perform validation that the objtype
+ * matches the prokind of the found function. For historical reasons
+ * we allow the objtype of FUNCTION to mean other things, but we draw
+ * the line if the object is a procedure. This is a new enough
+ * feature that this historical rule does not apply.
+ */
+ switch (objtype)
{
- if (noError)
- return InvalidOid;
- else if (func->args_unspecified)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not find a procedure named \"%s\"",
- NameListToString(func->objname))));
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("procedure %s does not exist",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
- }
+ case OBJECT_FUNCTION:
+ /* Only complain if it's a procedure. */
+ if (get_func_prokind(oid) == PROKIND_PROCEDURE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s is not a function",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
- /* Make sure it's a procedure */
- if (get_func_prokind(oid) != PROKIND_PROCEDURE)
- {
- if (noError)
- return InvalidOid;
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("%s is not a procedure",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
+ case OBJECT_PROCEDURE:
+ /* Reject found object is not a procedure */
+ if (get_func_prokind(oid) != PROKIND_PROCEDURE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s is not a procedure",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+
+ case OBJECT_AGGREGATE:
+ /* Reject found object is not an aggregate */
+ if (get_func_prokind(oid) != PROKIND_AGGREGATE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s is not an aggregate",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+
+ default:
+ /* Don't complain for routines */
+ break;
}
+
+ return oid; /* All good */
}
- else if (objtype == OBJECT_AGGREGATE)
+
+ /* Deal with cases where the lookup failed */
+ else
{
- if (!OidIsValid(oid))
+ switch (lookupError)
{
- if (noError)
+ case FUNCLOOKUP_AMBIGUOUS:
+ switch (objtype)
+ {
+ case OBJECT_FUNCTION:
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("function name \"%s\" is not unique",
+ NameListToString(func->objname)),
+ errhint("Specify the argument list to select the function unambiguously.")));
+ break;
+ case OBJECT_PROCEDURE:
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("procedure name \"%s\" is not unique",
+ NameListToString(func->objname)),
+ errhint("Specify the argument list to select the procedure unambiguously.")));
+ break;
+ case OBJECT_AGGREGATE:
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("aggregate name \"%s\" is not unique",
+ NameListToString(func->objname)),
+ errhint("Specify the argument list to select the aggregate unambiguously.")));
+ break;
+ case OBJECT_ROUTINE:
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("routine name \"%s\" is not unique",
+ NameListToString(func->objname)),
+ errhint("Specify the argument list to select the routine unambiguously.")));
+ break;
+
+ default:
+ Assert(false); /* Disallowed by Assert above */
+ break;
+ }
return InvalidOid;
- else if (func->args_unspecified)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not find an aggregate named \"%s\"",
- NameListToString(func->objname))));
- else if (argcount == 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("aggregate %s(*) does not exist",
- NameListToString(func->objname))));
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("aggregate %s does not exist",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
- }
- /* Make sure it's an aggregate */
- if (get_func_prokind(oid) != PROKIND_AGGREGATE)
- {
- if (noError)
- return InvalidOid;
- /* we do not use the (*) notation for functions... */
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("function %s is not an aggregate",
- func_signature_string(func->objname, argcount,
- NIL, argoids))));
+ case FUNCLOOKUP_NOSUCHFUNC:
+
+ /* Suppress no such func errors when missing_ok is true */
+ if (missing_ok)
+ break;
+
+ switch (objtype)
+ {
+ case OBJECT_PROCEDURE:
+ if (func->args_unspecified)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find a procedure named \"%s\"",
+ NameListToString(func->objname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("procedure %s does not exist",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+
+ case OBJECT_AGGREGATE:
+ if (func->args_unspecified)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find an aggregate named \"%s\"",
+ NameListToString(func->objname))));
+ else if (argcount == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("aggregate %s(*) does not exist",
+ NameListToString(func->objname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("aggregate %s does not exist",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+
+ default:
+ /* FUNCTION and ROUTINE */
+ if (func->args_unspecified)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not find a function named \"%s\"",
+ NameListToString(func->objname))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(func->objname, argcount,
+ NIL, argoids))));
+ break;
+ }
}
+ return InvalidOid;
}
-
- return oid;
}
/*
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index ff1477e64f..743e6668f0 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -63,9 +63,9 @@ 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 missing_ok);
extern Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
- bool noError);
+ bool missing_ok);
extern void check_srf_call_placement(ParseState *pstate, Node *last_srf,
int location);
diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out
index b80c5ed2d8..488678fa41 100644
--- a/src/test/regress/expected/drop_if_exists.out
+++ b/src/test/regress/expected/drop_if_exists.out
@@ -138,6 +138,31 @@ 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);
+-- Ensure we get an ambiguous function error for procedures too.
+CREATE PROCEDURE test_ambiguous_procname(int) as $$ begin end; $$ language plpgsql;
+CREATE PROCEDURE test_ambiguous_procname(text) as $$ begin end; $$ language plpgsql;
+DROP PROCEDURE IF EXISTS test_ambiguous_procname;
+ERROR: procedure name "test_ambiguous_procname" is not unique
+HINT: Specify the argument list to select the procedure unambiguously.
+-- Ensure we get a similar error if we use ROUTINE instead of PROCEDURE.
+DROP ROUTINE IF EXISTS test_ambiguous_procname;
+ERROR: routine name "test_ambiguous_procname" is not unique
+HINT: Specify the argument list to select the routine unambiguously.
+-- cleanup
+DROP FUNCTION test_ambiguous_procname(int);
+ERROR: test_ambiguous_procname(integer) is not a function
+DROP FUNCTION test_ambiguous_procname(text);
+ERROR: test_ambiguous_procname(text) is not a function
-- 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..451fa6ea35 100644
--- a/src/test/regress/sql/drop_if_exists.sql
+++ b/src/test/regress/sql/drop_if_exists.sql
@@ -153,6 +153,28 @@ 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);
+
+-- Ensure we get an ambiguous function error for procedures too.
+CREATE PROCEDURE test_ambiguous_procname(int) as $$ begin end; $$ language plpgsql;
+CREATE PROCEDURE test_ambiguous_procname(text) as $$ begin end; $$ language plpgsql;
+DROP PROCEDURE IF EXISTS test_ambiguous_procname;
+
+-- Ensure we get a similar error if we use ROUTINE instead of PROCEDURE.
+DROP ROUTINE IF EXISTS test_ambiguous_procname;
+
+-- cleanup
+DROP FUNCTION test_ambiguous_procname(int);
+DROP FUNCTION test_ambiguous_procname(text);
+
-- aggregate
DROP AGGREGATE test_aggregate_exists(*);
DROP AGGREGATE IF EXISTS test_aggregate_exists(*);
David Rowley <david.rowley@2ndquadrant.com> writes:
[ drop_func_if_not_exists_fix_v9.patch ]
Pushed with mostly-cosmetic adjustments.
I noticed a couple of loose ends that are somewhat outside the scope
of the bug report, but maybe are worth considering now:
1. There's some inconsistency in the wording of the error messages in
these routines, eg
errmsg("%s is not a function",
vs
errmsg("%s is not a procedure",
vs
errmsg("function %s is not an aggregate",
Also there's
errmsg("function name \"%s\" is not unique",
where elsewhere in parse_func.c, we find
errmsg("function %s is not unique",
You didn't touch this and I didn't either, but maybe we should try to
make these consistent?
2. Consider
regression=# CREATE FUNCTION ambig(int) returns int as $$ select $1; $$ language sql;
CREATE FUNCTION
regression=# CREATE PROCEDURE ambig() as $$ begin end; $$ language plpgsql;
CREATE PROCEDURE
regression=# DROP PROCEDURE ambig;
ERROR: procedure name "ambig" is not unique
HINT: Specify the argument list to select the procedure unambiguously.
Arguably, because I said "drop procedure", there's no ambiguity here;
but we don't account for objtype while doing the lookup.
I'm inclined to leave point 2 alone, because we haven't had complaints
about it, and because I'm not sure we could make it behave in a clean
way given the historical ambiguity about what OBJECT_FUNCTION should
match. But perhaps it's worth discussing.
regards, tom lane
On Fri, 22 Mar 2019 at 05:04, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Pushed with mostly-cosmetic adjustments.
Thanks for pushing this.
I noticed a couple of loose ends that are somewhat outside the scope
of the bug report, but maybe are worth considering now:1. There's some inconsistency in the wording of the error messages in
these routines, egerrmsg("%s is not a function",
vs
errmsg("%s is not a procedure",
vs
errmsg("function %s is not an aggregate",Also there's
errmsg("function name \"%s\" is not unique",
where elsewhere in parse_func.c, we find
errmsg("function %s is not unique",You didn't touch this and I didn't either, but maybe we should try to
make these consistent?
I think aligning those is a good idea. I had just been wondering to
myself last night about how much binary space is taken up by needless
additional string constants that could be normalised a bit.
Translators might be happy if we did that.
2. Consider
regression=# CREATE FUNCTION ambig(int) returns int as $$ select $1; $$ language sql;
CREATE FUNCTION
regression=# CREATE PROCEDURE ambig() as $$ begin end; $$ language plpgsql;
CREATE PROCEDURE
regression=# DROP PROCEDURE ambig;
ERROR: procedure name "ambig" is not unique
HINT: Specify the argument list to select the procedure unambiguously.Arguably, because I said "drop procedure", there's no ambiguity here;
but we don't account for objtype while doing the lookup.
Yeah. I went with reporting the objtype that was specified in a
command. I stayed well clear of allowing overlapping names between
procedures and functions. It would be hard to put that back if we
ever discovered a reason we shouldn't have done it.
--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services