diff --git a/doc/src/sgml/ref/alter_extension.sgml b/doc/src/sgml/ref/alter_extension.sgml index de6d6dca16b..a7c0927d1c9 100644 --- a/doc/src/sgml/ref/alter_extension.sgml +++ b/doc/src/sgml/ref/alter_extension.sgml @@ -39,7 +39,7 @@ ALTER EXTENSION name DROP object_name | FOREIGN DATA WRAPPER object_name | FOREIGN TABLE object_name | - FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | + FUNCTION function_name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] | MATERIALIZED VIEW object_name | OPERATOR operator_name (left_type, right_type) | OPERATOR CLASS object_name USING index_method | diff --git a/doc/src/sgml/ref/alter_function.sgml b/doc/src/sgml/ref/alter_function.sgml index 0388d06b959..168eeb7c526 100644 --- a/doc/src/sgml/ref/alter_function.sgml +++ b/doc/src/sgml/ref/alter_function.sgml @@ -21,15 +21,15 @@ PostgreSQL documentation -ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) +ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] action [ ... ] [ RESTRICT ] -ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) +ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] RENAME TO new_name -ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) +ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] OWNER TO { new_owner | CURRENT_USER | SESSION_USER } -ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) +ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] SET SCHEMA new_schema -ALTER FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) +ALTER FUNCTION name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] DEPENDS ON EXTENSION extension_name where action is one of: @@ -75,7 +75,8 @@ ALTER FUNCTION name ( [ [ name - The name (optionally schema-qualified) of an existing function. + The name (optionally schema-qualified) of an existing function. If no + argument list is specified, the name must be unique in its schema. diff --git a/doc/src/sgml/ref/alter_opfamily.sgml b/doc/src/sgml/ref/alter_opfamily.sgml index 4511c7f7b24..0bafe5b8f80 100644 --- a/doc/src/sgml/ref/alter_opfamily.sgml +++ b/doc/src/sgml/ref/alter_opfamily.sgml @@ -25,7 +25,7 @@ ALTER OPERATOR FAMILY name USING strategy_number operator_name ( op_type, op_type ) [ FOR SEARCH | FOR ORDER BY sort_family_name ] | FUNCTION support_number [ ( op_type [ , op_type ] ) ] - function_name ( argument_type [, ...] ) + function_name [ ( argument_type [, ...] ) ] } [, ... ] ALTER OPERATOR FAMILY name USING index_method DROP @@ -195,8 +195,9 @@ ALTER OPERATOR FAMILY name USING function_name - The name (optionally schema-qualified) of a function that is an - index method support procedure for the operator family. + The name (optionally schema-qualified) of a function that is an index + method support procedure for the operator family. If no argument list + is specified, the name must be unique in its schema. diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index c1cf587cb29..7483c8c03fc 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -37,7 +37,7 @@ COMMENT ON EVENT TRIGGER object_name | FOREIGN DATA WRAPPER object_name | FOREIGN TABLE object_name | - FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | + FUNCTION function_name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] | INDEX object_name | LARGE OBJECT large_object_oid | MATERIALIZED VIEW object_name | diff --git a/doc/src/sgml/ref/create_cast.sgml b/doc/src/sgml/ref/create_cast.sgml index 11266755e56..a7d13edc22b 100644 --- a/doc/src/sgml/ref/create_cast.sgml +++ b/doc/src/sgml/ref/create_cast.sgml @@ -19,7 +19,7 @@ CREATE CAST (source_type AS target_type) - WITH FUNCTION function_name (argument_type [, ...]) + WITH FUNCTION function_name [ (argument_type [, ...]) ] [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (source_type AS target_type) @@ -192,7 +192,7 @@ SELECT CAST ( 2 AS numeric ) + 4.0; - function_name(argument_type [, ...]) + function_name[(argument_type [, ...])] @@ -200,6 +200,8 @@ SELECT CAST ( 2 AS numeric ) + 4.0; be schema-qualified. If it is not, the function will be looked up in the schema search path. The function's result data type must match the target type of the cast. Its arguments are discussed below. + If no argument list is specified, the function name must be unique in + its schema. diff --git a/doc/src/sgml/ref/create_transform.sgml b/doc/src/sgml/ref/create_transform.sgml index f44ee89d33c..647c3b9f057 100644 --- a/doc/src/sgml/ref/create_transform.sgml +++ b/doc/src/sgml/ref/create_transform.sgml @@ -19,8 +19,8 @@ CREATE [ OR REPLACE ] TRANSFORM FOR type_name LANGUAGE lang_name ( - FROM SQL WITH FUNCTION from_sql_function_name (argument_type [, ...]), - TO SQL WITH FUNCTION to_sql_function_name (argument_type [, ...]) + FROM SQL WITH FUNCTION from_sql_function_name [ (argument_type [, ...]) ], + TO SQL WITH FUNCTION to_sql_function_name [ (argument_type [, ...]) ] ); @@ -104,7 +104,7 @@ CREATE [ OR REPLACE ] TRANSFORM FOR type_name LANGUAG - from_sql_function_name(argument_type [, ...]) + from_sql_function_name[(argument_type [, ...])] @@ -116,12 +116,14 @@ CREATE [ OR REPLACE ] TRANSFORM FOR type_name LANGUAG SQL-level function returning internal without at least one argument of type internal.) The actual return value will be something specific to the language implementation. + If no argument list is specified, the function name must be unique in + its schema. - to_sql_function_name(argument_type [, ...]) + to_sql_function_name[(argument_type [, ...])] @@ -130,6 +132,8 @@ CREATE [ OR REPLACE ] TRANSFORM FOR type_name LANGUAG internal and return the type that is the type for the transform. The actual argument value will be something specific to the language implementation. + If no argument list is specified, the function name must be unique in + its schema. diff --git a/doc/src/sgml/ref/drop_function.sgml b/doc/src/sgml/ref/drop_function.sgml index 5969b084b44..0aa984528d3 100644 --- a/doc/src/sgml/ref/drop_function.sgml +++ b/doc/src/sgml/ref/drop_function.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -DROP FUNCTION [ IF EXISTS ] name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) [, ...] +DROP FUNCTION [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...] [ CASCADE | RESTRICT ] @@ -56,7 +56,8 @@ DROP FUNCTION [ IF EXISTS ] name ( name - The name (optionally schema-qualified) of an existing function. + The name (optionally schema-qualified) of an existing function. If no + argument list is specified, the name must be unique in its schema. @@ -141,14 +142,40 @@ DROP FUNCTION sqrt(integer); DROP FUNCTION sqrt(integer), sqrt(bigint); + + + If the function name is unique in its schema, it can be referred to without + an argument list: + +DROP FUNCTION update_employee_salaries; + + Note that this is different from + +DROP FUNCTION update_employee_salaries(); + + which refers to a function with zero arguments, whereas the first variant + can refer to a function with any number of arguments, including zero, as + long as the name is unique. + Compatibility - A DROP FUNCTION statement is defined in the SQL - standard, but it is not compatible with this command. + This command conforms to the SQL standard, with + these PostgreSQL extensions: + + + The standard only allows one function to be dropped per command. + + + The IF EXISTS option + + + The ability to specify argument modes and names + + diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml index d8ca39f869e..9fb4c2fd7e3 100644 --- a/doc/src/sgml/ref/grant.sgml +++ b/doc/src/sgml/ref/grant.sgml @@ -55,7 +55,7 @@ GRANT { USAGE | ALL [ PRIVILEGES ] } TO role_specification [, ...] [ WITH GRANT OPTION ] GRANT { EXECUTE | ALL [ PRIVILEGES ] } - ON { FUNCTION function_name ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) [, ...] + ON { FUNCTION function_name [ ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) ] [, ...] | ALL FUNCTIONS IN SCHEMA schema_name [, ...] } TO role_specification [, ...] [ WITH GRANT OPTION ] diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml index fc00129620b..ce532543f0b 100644 --- a/doc/src/sgml/ref/revoke.sgml +++ b/doc/src/sgml/ref/revoke.sgml @@ -70,7 +70,7 @@ REVOKE [ GRANT OPTION FOR ] REVOKE [ GRANT OPTION FOR ] { EXECUTE | ALL [ PRIVILEGES ] } - ON { FUNCTION function_name ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) [, ...] + ON { FUNCTION function_name [ ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) ] [, ...] | ALL FUNCTIONS IN SCHEMA schema_name [, ...] } FROM { [ GROUP ] role_name | PUBLIC } [, ...] [ CASCADE | RESTRICT ] diff --git a/doc/src/sgml/ref/security_label.sgml b/doc/src/sgml/ref/security_label.sgml index 998fe3b7c03..afd86aff3a0 100644 --- a/doc/src/sgml/ref/security_label.sgml +++ b/doc/src/sgml/ref/security_label.sgml @@ -30,7 +30,7 @@ SECURITY LABEL [ FOR provider ] ON DOMAIN object_name | EVENT TRIGGER object_name | FOREIGN TABLE object_name - FUNCTION function_name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) | + FUNCTION function_name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] | LARGE OBJECT large_object_oid | MATERIALIZED VIEW object_name | [ PROCEDURAL ] LANGUAGE object_name | diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index bfc2ac17165..25fd051d6ef 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3067,6 +3067,7 @@ _copyObjectWithArgs(const ObjectWithArgs *from) COPY_NODE_FIELD(objname); COPY_NODE_FIELD(objargs); + COPY_SCALAR_FIELD(args_unspecified); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 54e9c983a0f..67529e3f861 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1119,6 +1119,7 @@ _equalObjectWithArgs(const ObjectWithArgs *a, const ObjectWithArgs *b) { COMPARE_NODE_FIELD(objname); COMPARE_NODE_FIELD(objargs); + COMPARE_SCALAR_FIELD(args_unspecified); return true; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index e7acc2d9a23..6316688a883 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -7202,6 +7202,33 @@ function_with_argtypes: n->objargs = extractArgTypes($2); $$ = n; } + /* + * Because of reduce/reduce conflicts, we can't use func_name + * below, but we can write it out the long way, which actually + * allows more cases. + */ + | type_func_name_keyword + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = list_make1(makeString(pstrdup($1))); + n->args_unspecified = true; + $$ = n; + } + | ColId + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = list_make1(makeString($1)); + n->args_unspecified = true; + $$ = n; + } + | ColId indirection + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = check_func_name(lcons(makeString($1), $2), + yyscanner); + n->args_unspecified = true; + $$ = n; + } ; /* diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index dd9749f2056..55853c20bb4 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -1895,8 +1895,10 @@ func_signature_string(List *funcname, int nargs, /* * LookupFuncName - * Given a possibly-qualified function name and a set of argument types, - * look up the function. + * + * 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. @@ -1914,6 +1916,35 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, noError); + /* + * If no arguments were specified, the name must yield a unique candidate. + */ + if (nargs == -1) + { + if (clist) + { + 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."))); + } + else + return clist->oid; + } + else + { + if (!noError) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not find a function named \"%s\"", + NameListToString(funcname)))); + } + } + while (clist) { if (memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0) @@ -1962,7 +1993,7 @@ LookupFuncWithArgs(ObjectWithArgs *func, bool noError) args_item = lnext(args_item); } - return LookupFuncName(func->objname, argcount, argoids, noError); + return LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids, noError); } /* diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index a44d2178e1c..d576523f6a8 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1811,6 +1811,9 @@ typedef struct ObjectWithArgs NodeTag type; List *objname; /* qualified name of function/operator */ List *objargs; /* list of Typename nodes */ + bool args_unspecified; /* argument list was omitted, so name must + * be unique (note that objargs == NIL means + * zero args) */ } ObjectWithArgs; /* diff --git a/src/test/regress/expected/create_function_3.out b/src/test/regress/expected/create_function_3.out index cc4e98a1d4b..b5e19485e56 100644 --- a/src/test/regress/expected/create_function_3.out +++ b/src/test/regress/expected/create_function_3.out @@ -218,13 +218,21 @@ SELECT routine_name, ordinal_position, parameter_name, parameter_default (7 rows) DROP FUNCTION functest_IS_1(int, int, text), functest_IS_2(int), functest_IS_3(int); +-- overload +CREATE FUNCTION functest_B_2(bigint) RETURNS bool LANGUAGE 'sql' + IMMUTABLE AS 'SELECT $1 > 0'; +DROP FUNCTION functest_b_1; +DROP FUNCTION functest_b_1; -- error, not found +ERROR: could not find a function named "functest_b_1" +DROP FUNCTION functest_b_2; -- error, ambiguous +ERROR: function name "functest_b_2" is not unique +HINT: Specify the argument list to select the function unambiguously. -- Cleanups DROP SCHEMA temp_func_test CASCADE; NOTICE: drop cascades to 16 other objects DETAIL: drop cascades to function functest_a_1(text,date) drop cascades to function functest_a_2(text[]) drop cascades to function functest_a_3() -drop cascades to function functest_b_1(integer) drop cascades to function functest_b_2(integer) drop cascades to function functest_b_3(integer) drop cascades to function functest_b_4(integer) @@ -237,5 +245,6 @@ drop cascades to function functext_f_1(integer) drop cascades to function functext_f_2(integer) drop cascades to function functext_f_3(integer) drop cascades to function functext_f_4(integer) +drop cascades to function functest_b_2(bigint) DROP USER regress_unpriv_user; RESET search_path; diff --git a/src/test/regress/sql/create_function_3.sql b/src/test/regress/sql/create_function_3.sql index 66a463b0895..0a0e407aaba 100644 --- a/src/test/regress/sql/create_function_3.sql +++ b/src/test/regress/sql/create_function_3.sql @@ -158,6 +158,14 @@ SELECT routine_name, ordinal_position, parameter_name, parameter_default DROP FUNCTION functest_IS_1(int, int, text), functest_IS_2(int), functest_IS_3(int); +-- overload +CREATE FUNCTION functest_B_2(bigint) RETURNS bool LANGUAGE 'sql' + IMMUTABLE AS 'SELECT $1 > 0'; + +DROP FUNCTION functest_b_1; +DROP FUNCTION functest_b_1; -- error, not found +DROP FUNCTION functest_b_2; -- error, ambiguous + -- Cleanups DROP SCHEMA temp_func_test CASCADE;