drop_func_if_not_exists_fix_v8.patch
application/octet-stream
Filename: drop_func_if_not_exists_fix_v8.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
Series: patch v8
| File | + | − |
|---|---|---|
| src/backend/parser/parse_func.c | 240 | 108 |
| src/test/regress/expected/drop_if_exists.out | 21 | 0 |
| src/test/regress/sql/drop_if_exists.sql | 19 | 0 |
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(*);