diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 92 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 109 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 207 | ||||
-rw-r--r-- | src/backend/parser/parse_oper.c | 4 |
4 files changed, 294 insertions, 118 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 9cede29d6a8..438b077004d 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -25,6 +25,7 @@ #include "postgres.h" #include "access/sysattr.h" +#include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -50,6 +51,7 @@ #include "utils/guc.h" #include "utils/queryjumble.h" #include "utils/rel.h" +#include "utils/syscache.h" /* Hook for plugins to get control at end of parse analysis */ @@ -2933,8 +2935,6 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) /* * transform a CallStmt - * - * We need to do parse analysis on the procedure call and its arguments. */ static Query * transformCallStmt(ParseState *pstate, CallStmt *stmt) @@ -2942,8 +2942,17 @@ transformCallStmt(ParseState *pstate, CallStmt *stmt) List *targs; ListCell *lc; Node *node; + FuncExpr *fexpr; + HeapTuple proctup; + Datum proargmodes; + bool isNull; + List *outargs = NIL; Query *result; + /* + * First, do standard parse analysis on the procedure call and its + * arguments, allowing us to identify the called procedure. + */ targs = NIL; foreach(lc, stmt->funccall->args) { @@ -2962,8 +2971,85 @@ transformCallStmt(ParseState *pstate, CallStmt *stmt) assign_expr_collations(pstate, node); - stmt->funcexpr = castNode(FuncExpr, node); + fexpr = castNode(FuncExpr, node); + + proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup failed for function %u", fexpr->funcid); + + /* + * Expand the argument list to deal with named-argument notation and + * default arguments. For ordinary FuncExprs this'd be done during + * planning, but a CallStmt doesn't go through planning, and there seems + * no good reason not to do it here. + */ + fexpr->args = expand_function_arguments(fexpr->args, + true, + fexpr->funcresulttype, + proctup); + + /* Fetch proargmodes; if it's null, there are no output args */ + proargmodes = SysCacheGetAttr(PROCOID, proctup, + Anum_pg_proc_proargmodes, + &isNull); + if (!isNull) + { + /* + * Split the list into input arguments in fexpr->args and output + * arguments in stmt->outargs. INOUT arguments appear in both lists. + */ + ArrayType *arr; + int numargs; + char *argmodes; + List *inargs; + int i; + + arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ + numargs = list_length(fexpr->args); + if (ARR_NDIM(arr) != 1 || + ARR_DIMS(arr)[0] != numargs || + ARR_HASNULL(arr) || + ARR_ELEMTYPE(arr) != CHAROID) + elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls", + numargs); + argmodes = (char *) ARR_DATA_PTR(arr); + + inargs = NIL; + i = 0; + foreach(lc, fexpr->args) + { + Node *n = lfirst(lc); + + switch (argmodes[i]) + { + case PROARGMODE_IN: + case PROARGMODE_VARIADIC: + inargs = lappend(inargs, n); + break; + case PROARGMODE_OUT: + outargs = lappend(outargs, n); + break; + case PROARGMODE_INOUT: + inargs = lappend(inargs, n); + outargs = lappend(outargs, copyObject(n)); + break; + default: + /* note we don't support PROARGMODE_TABLE */ + elog(ERROR, "invalid argmode %c for procedure", + argmodes[i]); + break; + } + i++; + } + fexpr->args = inargs; + } + stmt->funcexpr = fexpr; + stmt->outargs = outargs; + + ReleaseSysCache(proctup); + + /* represent the command as a utility Query */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 52a254928f8..eb241954387 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -172,7 +172,7 @@ static RoleSpec *makeRoleSpec(RoleSpecType type, int location); static void check_qualified_name(List *names, core_yyscan_t yyscanner); static List *check_func_name(List *names, core_yyscan_t yyscanner); static List *check_indirection(List *indirection, core_yyscan_t yyscanner); -static List *extractArgTypes(ObjectType objtype, List *parameters); +static List *extractArgTypes(List *parameters); static List *extractAggrArgTypes(List *aggrargs); static List *makeOrderedSetArgs(List *directargs, List *orderedargs, core_yyscan_t yyscanner); @@ -385,8 +385,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <accesspriv> privilege %type <list> privileges privilege_list %type <privtarget> privilege_target -%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes procedure_with_argtypes function_with_argtypes_common -%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list procedure_with_argtypes_list +%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes +%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list %type <ival> defacl_privilege_target %type <defelt> DefACLOption %type <list> DefACLOptionList @@ -4757,7 +4757,7 @@ AlterExtensionContentsStmt: n->object = (Node *) lcons(makeString($9), $7); $$ = (Node *)n; } - | ALTER EXTENSION name add_drop PROCEDURE procedure_with_argtypes + | ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); n->extname = $3; @@ -4766,7 +4766,7 @@ AlterExtensionContentsStmt: n->object = (Node *) $6; $$ = (Node *)n; } - | ALTER EXTENSION name add_drop ROUTINE procedure_with_argtypes + | ALTER EXTENSION name add_drop ROUTINE function_with_argtypes { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); n->extname = $3; @@ -6505,7 +6505,7 @@ CommentStmt: n->comment = $8; $$ = (Node *) n; } - | COMMENT ON PROCEDURE procedure_with_argtypes IS comment_text + | COMMENT ON PROCEDURE function_with_argtypes IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_PROCEDURE; @@ -6513,7 +6513,7 @@ CommentStmt: n->comment = $6; $$ = (Node *) n; } - | COMMENT ON ROUTINE procedure_with_argtypes IS comment_text + | COMMENT ON ROUTINE function_with_argtypes IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_ROUTINE; @@ -6659,7 +6659,7 @@ SecLabelStmt: n->label = $9; $$ = (Node *) n; } - | SECURITY LABEL opt_provider ON PROCEDURE procedure_with_argtypes + | SECURITY LABEL opt_provider ON PROCEDURE function_with_argtypes IS security_label { SecLabelStmt *n = makeNode(SecLabelStmt); @@ -7023,7 +7023,7 @@ privilege_target: n->objs = $2; $$ = n; } - | PROCEDURE procedure_with_argtypes_list + | PROCEDURE function_with_argtypes_list { PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); n->targtype = ACL_TARGET_OBJECT; @@ -7031,7 +7031,7 @@ privilege_target: n->objs = $2; $$ = n; } - | ROUTINE procedure_with_argtypes_list + | ROUTINE function_with_argtypes_list { PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); n->targtype = ACL_TARGET_OBJECT; @@ -7556,33 +7556,21 @@ function_with_argtypes_list: { $$ = lappend($1, $3); } ; -procedure_with_argtypes_list: - procedure_with_argtypes { $$ = list_make1($1); } - | procedure_with_argtypes_list ',' procedure_with_argtypes - { $$ = lappend($1, $3); } - ; - function_with_argtypes: func_name func_args { ObjectWithArgs *n = makeNode(ObjectWithArgs); n->objname = $1; - n->objargs = extractArgTypes(OBJECT_FUNCTION, $2); + n->objargs = extractArgTypes($2); + n->objfuncargs = $2; $$ = n; } - | function_with_argtypes_common - { - $$ = $1; - } - ; - -function_with_argtypes_common: /* * 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 + | type_func_name_keyword { ObjectWithArgs *n = makeNode(ObjectWithArgs); n->objname = list_make1(makeString(pstrdup($1))); @@ -7607,24 +7595,6 @@ function_with_argtypes_common: ; /* - * This is different from function_with_argtypes in the call to - * extractArgTypes(). - */ -procedure_with_argtypes: - func_name func_args - { - ObjectWithArgs *n = makeNode(ObjectWithArgs); - n->objname = $1; - n->objargs = extractArgTypes(OBJECT_PROCEDURE, $2); - $$ = n; - } - | function_with_argtypes_common - { - $$ = $1; - } - ; - -/* * func_args_with_defaults is separate because we only want to accept * defaults in CREATE FUNCTION, not in ALTER etc. */ @@ -7673,7 +7643,7 @@ func_arg: FunctionParameter *n = makeNode(FunctionParameter); n->name = $1; n->argType = $2; - n->mode = FUNC_PARAM_IN; + n->mode = FUNC_PARAM_DEFAULT; n->defexpr = NULL; $$ = n; } @@ -7691,7 +7661,7 @@ func_arg: FunctionParameter *n = makeNode(FunctionParameter); n->name = NULL; n->argType = $1; - n->mode = FUNC_PARAM_IN; + n->mode = FUNC_PARAM_DEFAULT; n->defexpr = NULL; $$ = n; } @@ -7763,7 +7733,8 @@ func_arg_with_default: /* Aggregate args can be most things that function args can be */ aggr_arg: func_arg { - if (!($1->mode == FUNC_PARAM_IN || + if (!($1->mode == FUNC_PARAM_DEFAULT || + $1->mode == FUNC_PARAM_IN || $1->mode == FUNC_PARAM_VARIADIC)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -7832,6 +7803,7 @@ aggregate_with_argtypes: ObjectWithArgs *n = makeNode(ObjectWithArgs); n->objname = $1; n->objargs = extractAggrArgTypes($2); + n->objfuncargs = (List *) linitial($2); $$ = n; } ; @@ -8056,7 +8028,7 @@ AlterFunctionStmt: n->actions = $4; $$ = (Node *) n; } - | ALTER PROCEDURE procedure_with_argtypes alterfunc_opt_list opt_restrict + | ALTER PROCEDURE function_with_argtypes alterfunc_opt_list opt_restrict { AlterFunctionStmt *n = makeNode(AlterFunctionStmt); n->objtype = OBJECT_PROCEDURE; @@ -8064,7 +8036,7 @@ AlterFunctionStmt: n->actions = $4; $$ = (Node *) n; } - | ALTER ROUTINE procedure_with_argtypes alterfunc_opt_list opt_restrict + | ALTER ROUTINE function_with_argtypes alterfunc_opt_list opt_restrict { AlterFunctionStmt *n = makeNode(AlterFunctionStmt); n->objtype = OBJECT_ROUTINE; @@ -8120,7 +8092,7 @@ RemoveFuncStmt: n->concurrent = false; $$ = (Node *)n; } - | DROP PROCEDURE procedure_with_argtypes_list opt_drop_behavior + | DROP PROCEDURE function_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_PROCEDURE; @@ -8130,7 +8102,7 @@ RemoveFuncStmt: n->concurrent = false; $$ = (Node *)n; } - | DROP PROCEDURE IF_P EXISTS procedure_with_argtypes_list opt_drop_behavior + | DROP PROCEDURE IF_P EXISTS function_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_PROCEDURE; @@ -8140,7 +8112,7 @@ RemoveFuncStmt: n->concurrent = false; $$ = (Node *)n; } - | DROP ROUTINE procedure_with_argtypes_list opt_drop_behavior + | DROP ROUTINE function_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_ROUTINE; @@ -8150,7 +8122,7 @@ RemoveFuncStmt: n->concurrent = false; $$ = (Node *)n; } - | DROP ROUTINE IF_P EXISTS procedure_with_argtypes_list opt_drop_behavior + | DROP ROUTINE IF_P EXISTS function_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_ROUTINE; @@ -8622,7 +8594,7 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name n->missing_ok = true; $$ = (Node *)n; } - | ALTER PROCEDURE procedure_with_argtypes RENAME TO name + | ALTER PROCEDURE function_with_argtypes RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_PROCEDURE; @@ -8640,7 +8612,7 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name n->missing_ok = false; $$ = (Node *)n; } - | ALTER ROUTINE procedure_with_argtypes RENAME TO name + | ALTER ROUTINE function_with_argtypes RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_ROUTINE; @@ -9051,7 +9023,7 @@ AlterObjectDependsStmt: n->remove = $4; $$ = (Node *)n; } - | ALTER PROCEDURE procedure_with_argtypes opt_no DEPENDS ON EXTENSION name + | ALTER PROCEDURE function_with_argtypes opt_no DEPENDS ON EXTENSION name { AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); n->objectType = OBJECT_PROCEDURE; @@ -9060,7 +9032,7 @@ AlterObjectDependsStmt: n->remove = $4; $$ = (Node *)n; } - | ALTER ROUTINE procedure_with_argtypes opt_no DEPENDS ON EXTENSION name + | ALTER ROUTINE function_with_argtypes opt_no DEPENDS ON EXTENSION name { AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); n->objectType = OBJECT_ROUTINE; @@ -9191,7 +9163,7 @@ AlterObjectSchemaStmt: n->missing_ok = false; $$ = (Node *)n; } - | ALTER PROCEDURE procedure_with_argtypes SET SCHEMA name + | ALTER PROCEDURE function_with_argtypes SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_PROCEDURE; @@ -9200,7 +9172,7 @@ AlterObjectSchemaStmt: n->missing_ok = false; $$ = (Node *)n; } - | ALTER ROUTINE procedure_with_argtypes SET SCHEMA name + | ALTER ROUTINE function_with_argtypes SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_ROUTINE; @@ -9502,7 +9474,7 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec n->newowner = $9; $$ = (Node *)n; } - | ALTER PROCEDURE procedure_with_argtypes OWNER TO RoleSpec + | ALTER PROCEDURE function_with_argtypes OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_PROCEDURE; @@ -9510,7 +9482,7 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec n->newowner = $6; $$ = (Node *)n; } - | ALTER ROUTINE procedure_with_argtypes OWNER TO RoleSpec + | ALTER ROUTINE function_with_argtypes OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_ROUTINE; @@ -16698,14 +16670,13 @@ check_indirection(List *indirection, core_yyscan_t yyscanner) } /* extractArgTypes() - * * Given a list of FunctionParameter nodes, extract a list of just the - * argument types (TypeNames) for signature parameters only (e.g., only input - * parameters for functions). This is what is needed to look up an existing - * function, which is what is wanted by the productions that use this call. + * argument types (TypeNames) for input parameters only. This is what + * is needed to look up an existing function, which is what is wanted by + * the productions that use this call. */ static List * -extractArgTypes(ObjectType objtype, List *parameters) +extractArgTypes(List *parameters) { List *result = NIL; ListCell *i; @@ -16714,7 +16685,7 @@ extractArgTypes(ObjectType objtype, List *parameters) { FunctionParameter *p = (FunctionParameter *) lfirst(i); - if ((p->mode != FUNC_PARAM_OUT || objtype == OBJECT_PROCEDURE) && p->mode != FUNC_PARAM_TABLE) + if (p->mode != FUNC_PARAM_OUT && p->mode != FUNC_PARAM_TABLE) result = lappend(result, p->argType); } return result; @@ -16727,7 +16698,7 @@ static List * extractAggrArgTypes(List *aggrargs) { Assert(list_length(aggrargs) == 2); - return extractArgTypes(OBJECT_AGGREGATE, (List *) linitial(aggrargs)); + return extractArgTypes((List *) linitial(aggrargs)); } /* makeOrderedSetArgs() @@ -17023,7 +16994,9 @@ mergeTableFuncParameters(List *func_args, List *columns) { FunctionParameter *p = (FunctionParameter *) lfirst(lc); - if (p->mode != FUNC_PARAM_IN && p->mode != FUNC_PARAM_VARIADIC) + if (p->mode != FUNC_PARAM_DEFAULT && + p->mode != FUNC_PARAM_IN && + p->mode != FUNC_PARAM_VARIADIC) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("OUT and INOUT arguments aren't allowed in TABLE functions"))); diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index fb0ba58ff77..3cec8de7da8 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -48,9 +48,10 @@ 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); +static Oid LookupFuncNameInternal(ObjectType objtype, List *funcname, + int nargs, const Oid *argtypes, + bool include_out_arguments, bool missing_ok, + FuncLookupError *lookupError); /* @@ -82,7 +83,8 @@ static Oid LookupFuncNameInternal(List *funcname, int nargs, * contain any SRF calls, last_srf can just be pstate->p_last_srf. * * proc_call is true if we are considering a CALL statement, so that the - * name must resolve to a procedure name, not anything else. + * name must resolve to a procedure name, not anything else. This flag + * also specifies that the argument list includes any OUT-mode arguments. */ Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, @@ -263,7 +265,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, fdresult = func_get_detail(funcname, fargs, argnames, nargs, actual_arg_types, - !func_variadic, true, + !func_variadic, true, proc_call, &funcid, &rettype, &retset, &nvargs, &vatype, &declared_arg_types, &argdefaults); @@ -1395,6 +1397,7 @@ func_get_detail(List *funcname, Oid *argtypes, bool expand_variadic, bool expand_defaults, + bool include_out_arguments, Oid *funcid, /* return value */ Oid *rettype, /* return value */ bool *retset, /* return value */ @@ -1419,7 +1422,7 @@ func_get_detail(List *funcname, /* Get list of possible candidates from namespace search */ raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames, expand_variadic, expand_defaults, - false); + include_out_arguments, false); /* * Quickly check if there is an exact match to the input datatypes (there @@ -1667,7 +1670,7 @@ func_get_detail(List *funcname, defargnumbers = bms_add_member(defargnumbers, firstdefarg[i]); newdefaults = NIL; - i = pform->pronargs - pform->pronargdefaults; + i = best_candidate->nominalnargs - pform->pronargdefaults; foreach(lc, defaults) { if (bms_is_member(i, defargnumbers)) @@ -2041,12 +2044,15 @@ func_signature_string(List *funcname, int nargs, * * Possible errors: * FUNCLOOKUP_NOSUCHFUNC: we can't find a function of this name. - * FUNCLOOKUP_AMBIGUOUS: nargs == -1 and more than one function matches. + * FUNCLOOKUP_AMBIGUOUS: more than one function matches. */ static Oid -LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes, - bool missing_ok, FuncLookupError *lookupError) +LookupFuncNameInternal(ObjectType objtype, List *funcname, + int nargs, const Oid *argtypes, + bool include_out_arguments, bool missing_ok, + FuncLookupError *lookupError) { + Oid result = InvalidOid; FuncCandidateList clist; /* NULL argtypes allowed for nullary functions only */ @@ -2055,43 +2061,62 @@ LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes, /* Always set *lookupError, to forestall uninitialized-variable warnings */ *lookupError = FUNCLOOKUP_NOSUCHFUNC; + /* Get list of candidate objects */ clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, - missing_ok); + include_out_arguments, missing_ok); - /* - * If no arguments were specified, the name must yield a unique candidate. - */ - if (nargs < 0) + /* Scan list for a match to the arg types (if specified) and the objtype */ + for (; clist != NULL; clist = clist->next) { - if (clist) + /* Check arg type match, if specified */ + if (nargs >= 0) { - /* If there is a second match then it's ambiguous */ - if (clist->next) - { - *lookupError = FUNCLOOKUP_AMBIGUOUS; - return InvalidOid; - } - /* Otherwise return the match */ - return clist->oid; + /* if nargs==0, argtypes can be null; don't pass that to memcmp */ + if (nargs > 0 && + memcmp(argtypes, clist->args, nargs * sizeof(Oid)) != 0) + continue; } - else + + /* Check for duplicates reported by FuncnameGetCandidates */ + if (!OidIsValid(clist->oid)) + { + *lookupError = FUNCLOOKUP_AMBIGUOUS; return InvalidOid; - } + } - /* - * Otherwise, look for a match to the arg types. FuncnameGetCandidates - * has ensured that there's at most one match in the returned list. - */ - while (clist) - { - /* if nargs==0, argtypes can be null; don't pass that to memcmp */ - if (nargs == 0 || - memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0) - return clist->oid; - clist = clist->next; + /* Check objtype match, if specified */ + switch (objtype) + { + case OBJECT_FUNCTION: + case OBJECT_AGGREGATE: + /* Ignore procedures */ + if (get_func_prokind(clist->oid) == PROKIND_PROCEDURE) + continue; + break; + case OBJECT_PROCEDURE: + /* Ignore non-procedures */ + if (get_func_prokind(clist->oid) != PROKIND_PROCEDURE) + continue; + break; + case OBJECT_ROUTINE: + /* no restriction */ + break; + default: + Assert(false); + } + + /* Check for multiple matches */ + if (OidIsValid(result)) + { + *lookupError = FUNCLOOKUP_AMBIGUOUS; + return InvalidOid; + } + + /* OK, we have a candidate */ + result = clist->oid; } - return InvalidOid; + return result; } /* @@ -2111,6 +2136,10 @@ LookupFuncNameInternal(List *funcname, int nargs, const Oid *argtypes, * If nargs == -1 and multiple functions are found matching this function name * we will raise an ambiguous-function error, regardless of what missing_ok is * set to. + * + * Only functions will be found; procedures will be ignored even if they + * match the name and argument types. (However, we don't trouble to reject + * aggregates or window functions here.) */ Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok) @@ -2118,7 +2147,9 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok) Oid funcoid; FuncLookupError lookupError; - funcoid = LookupFuncNameInternal(funcname, nargs, argtypes, missing_ok, + funcoid = LookupFuncNameInternal(OBJECT_FUNCTION, + funcname, nargs, argtypes, + false, missing_ok, &lookupError); if (OidIsValid(funcoid)) @@ -2207,10 +2238,14 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok) FUNC_MAX_ARGS))); } + /* + * First, perform a lookup considering only input arguments (traditional + * Postgres rules). + */ i = 0; foreach(args_item, func->objargs) { - TypeName *t = (TypeName *) lfirst(args_item); + TypeName *t = lfirst_node(TypeName, args_item); argoids[i] = LookupTypeNameOid(NULL, t, missing_ok); if (!OidIsValid(argoids[i])) @@ -2224,9 +2259,83 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok) */ nargs = func->args_unspecified ? -1 : argcount; - oid = LookupFuncNameInternal(func->objname, nargs, argoids, missing_ok, + /* + * In args_unspecified mode, also tell LookupFuncNameInternal to consider + * the object type, since there seems no reason not to. However, if we + * have an argument list, disable the objtype check, because we'd rather + * complain about "object is of wrong type" than "object doesn't exist". + * (Note that with args, FuncnameGetCandidates will have ensured there's + * only one argtype match, so we're not risking an ambiguity failure via + * this choice.) + */ + oid = LookupFuncNameInternal(func->args_unspecified ? objtype : OBJECT_ROUTINE, + func->objname, nargs, argoids, + false, missing_ok, &lookupError); + /* + * If PROCEDURE or ROUTINE was specified, and we have an argument list + * that contains no parameter mode markers, and we didn't already discover + * that there's ambiguity, perform a lookup considering all arguments. + * (Note: for a zero-argument procedure, or in args_unspecified mode, the + * normal lookup is sufficient; so it's OK to require non-NIL objfuncargs + * to perform this lookup.) + */ + if ((objtype == OBJECT_PROCEDURE || objtype == OBJECT_ROUTINE) && + func->objfuncargs != NIL && + lookupError != FUNCLOOKUP_AMBIGUOUS) + { + bool have_param_mode = false; + + /* + * Check for non-default parameter mode markers. If there are any, + * then the command does not conform to SQL-spec syntax, so we may + * assume that the traditional Postgres lookup method of considering + * only input parameters is sufficient. (Note that because the spec + * doesn't have OUT arguments for functions, we also don't need this + * hack in FUNCTION or AGGREGATE mode.) + */ + foreach(args_item, func->objfuncargs) + { + FunctionParameter *fp = lfirst_node(FunctionParameter, args_item); + + if (fp->mode != FUNC_PARAM_DEFAULT) + { + have_param_mode = true; + break; + } + } + + if (!have_param_mode) + { + Oid poid; + + /* Without mode marks, objargs surely includes all params */ + Assert(list_length(func->objfuncargs) == argcount); + + /* For objtype == OBJECT_PROCEDURE, we can ignore non-procedures */ + poid = LookupFuncNameInternal(objtype, func->objname, + argcount, argoids, + true, missing_ok, + &lookupError); + + /* Combine results, handling ambiguity */ + if (OidIsValid(poid)) + { + if (OidIsValid(oid) && oid != poid) + { + /* oops, we got hits both ways, on different objects */ + oid = InvalidOid; + lookupError = FUNCLOOKUP_AMBIGUOUS; + } + else + oid = poid; + } + else if (lookupError == FUNCLOOKUP_AMBIGUOUS) + oid = InvalidOid; + } + } + if (OidIsValid(oid)) { /* @@ -2235,6 +2344,10 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok) * we allow the objtype of FUNCTION to include aggregates and window * functions; but we draw the line if the object is a procedure. That * is a new enough feature that this historical rule does not apply. + * + * (This check is partially redundant with the objtype check in + * LookupFuncNameInternal; but not entirely, since we often don't tell + * LookupFuncNameInternal to apply that check at all.) */ switch (objtype) { @@ -2345,28 +2458,32 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool missing_ok) (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("function name \"%s\" is not unique", NameListToString(func->objname)), - errhint("Specify the argument list to select the function unambiguously."))); + func->args_unspecified ? + errhint("Specify the argument list to select the function unambiguously.") : 0)); 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."))); + func->args_unspecified ? + errhint("Specify the argument list to select the procedure unambiguously.") : 0)); 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."))); + func->args_unspecified ? + errhint("Specify the argument list to select the aggregate unambiguously.") : 0)); 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."))); + func->args_unspecified ? + errhint("Specify the argument list to select the routine unambiguously.") : 0)); break; default: diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index c379d72fcec..4e460799903 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -150,8 +150,8 @@ LookupOperWithArgs(ObjectWithArgs *oper, bool noError) rightoid; Assert(list_length(oper->objargs) == 2); - oprleft = linitial(oper->objargs); - oprright = lsecond(oper->objargs); + oprleft = linitial_node(TypeName, oper->objargs); + oprright = lsecond_node(TypeName, oper->objargs); if (oprleft == NULL) leftoid = InvalidOid; |