I noticed that ParseFuncOrColumn isn't terribly thorough about rejecting non-procedure results when proc_call is true. Since the caller is just assuming that it gets back what it expects, that leads to core dumps or worse:
regression=# call int4(42); server closed the connection unexpectedly This probably means the server terminated abnormally Also, with the existing ad-hoc approach to this, not all the ereports are alike; the one for an aggregate lacks the HINT the others provide. Also, in the cases where it does successfully reject should-be-procedure or should-not-be-procedure, it reports ERRCODE_UNDEFINED_FUNCTION, which seems quite inappropriate from here: I'd expect ERRCODE_WRONG_OBJECT_TYPE. There's also a pre-existing oversight that when we get a result of FUNCDETAIL_COERCION, we should reject that if there's any aggregate decoration, but we fail to. The attached cleans all this up; any objections? regards, tom lane
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index ea5d521..21ddd5b 100644 *** a/src/backend/parser/parse_func.c --- b/src/backend/parser/parse_func.c *************** static Node *ParseComplexProjection(Pars *** 68,73 **** --- 68,76 ---- * last_srf should be a copy of pstate->p_last_srf from just before we * started transforming fargs. If the caller knows that fargs couldn't * 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. */ Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, *************** ParseFuncOrColumn(ParseState *pstate, Li *** 204,210 **** * the "function call" could be a projection. We also check that there * wasn't any aggregate or variadic decoration, nor an argument name. */ ! if (nargs == 1 && agg_order == NIL && agg_filter == NULL && !agg_star && !agg_distinct && over == NULL && !func_variadic && argnames == NIL && list_length(funcname) == 1) { --- 207,214 ---- * the "function call" could be a projection. We also check that there * wasn't any aggregate or variadic decoration, nor an argument name. */ ! if (nargs == 1 && !proc_call && ! agg_order == NIL && agg_filter == NULL && !agg_star && !agg_distinct && over == NULL && !func_variadic && argnames == NIL && list_length(funcname) == 1) { *************** ParseFuncOrColumn(ParseState *pstate, Li *** 253,273 **** cancel_parser_errposition_callback(&pcbstate); ! if (fdresult == FUNCDETAIL_COERCION) ! { ! /* ! * We interpreted it as a type coercion. coerce_type can handle these ! * cases, so why duplicate code... ! */ ! return coerce_type(pstate, linitial(fargs), ! actual_arg_types[0], rettype, -1, ! COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location); ! } ! else if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE) { /* ! * Normal function found; was there anything indicating it must be an ! * aggregate? */ if (agg_star) ereport(ERROR, --- 257,298 ---- cancel_parser_errposition_callback(&pcbstate); ! /* ! * Check for various wrong-kind-of-routine cases. ! */ ! ! /* If this is a CALL, reject things that aren't procedures */ ! if (proc_call && ! (fdresult == FUNCDETAIL_NORMAL || ! fdresult == FUNCDETAIL_AGGREGATE || ! fdresult == FUNCDETAIL_WINDOWFUNC || ! fdresult == FUNCDETAIL_COERCION)) ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("%s is not a procedure", ! func_signature_string(funcname, nargs, ! argnames, ! actual_arg_types)), ! errhint("To call a function, use SELECT."), ! parser_errposition(pstate, location))); ! /* Conversely, if not a CALL, reject procedures */ ! if (fdresult == FUNCDETAIL_PROCEDURE && !proc_call) ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("%s is a procedure", ! func_signature_string(funcname, nargs, ! argnames, ! actual_arg_types)), ! errhint("To call a procedure, use CALL."), ! parser_errposition(pstate, location))); ! ! if (fdresult == FUNCDETAIL_NORMAL || ! fdresult == FUNCDETAIL_PROCEDURE || ! fdresult == FUNCDETAIL_COERCION) { /* ! * In these cases, complain if there was anything indicating it must ! * be an aggregate or window function. */ if (agg_star) ereport(ERROR, *************** ParseFuncOrColumn(ParseState *pstate, Li *** 306,331 **** errmsg("OVER specified, but %s is not a window function nor an aggregate function", NameListToString(funcname)), parser_errposition(pstate, location))); ! if (fdresult == FUNCDETAIL_NORMAL && proc_call) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_FUNCTION), ! errmsg("%s is not a procedure", ! func_signature_string(funcname, nargs, ! argnames, ! actual_arg_types)), ! errhint("To call a function, use SELECT."), ! parser_errposition(pstate, location))); ! ! if (fdresult == FUNCDETAIL_PROCEDURE && !proc_call) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_FUNCTION), ! errmsg("%s is a procedure", ! func_signature_string(funcname, nargs, ! argnames, ! actual_arg_types)), ! errhint("To call a procedure, use CALL."), ! parser_errposition(pstate, location))); } else if (fdresult == FUNCDETAIL_AGGREGATE) { --- 331,344 ---- errmsg("OVER specified, but %s is not a window function nor an aggregate function", NameListToString(funcname)), parser_errposition(pstate, location))); + } ! /* ! * So far so good, so do some routine-type-specific processing. ! */ ! if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE) ! { ! /* Nothing special to do for these cases. */ } else if (fdresult == FUNCDETAIL_AGGREGATE) { *************** ParseFuncOrColumn(ParseState *pstate, Li *** 336,350 **** Form_pg_aggregate classForm; int catDirectArgs; - if (proc_call) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("%s is not a procedure", - func_signature_string(funcname, nargs, - argnames, - actual_arg_types)), - parser_errposition(pstate, location))); - tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(tup)) /* should not happen */ elog(ERROR, "cache lookup failed for aggregate %u", funcid); --- 349,354 ---- *************** ParseFuncOrColumn(ParseState *pstate, Li *** 510,515 **** --- 514,529 ---- NameListToString(funcname)), parser_errposition(pstate, location))); } + else if (fdresult == FUNCDETAIL_COERCION) + { + /* + * We interpreted it as a type coercion. coerce_type can handle these + * cases, so why duplicate code... + */ + return coerce_type(pstate, linitial(fargs), + actual_arg_types[0], rettype, -1, + COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location); + } else { /* diff --git a/src/test/regress/expected/create_procedure.out b/src/test/regress/expected/create_procedure.out index 67d6717..3049597 100644 *** a/src/test/regress/expected/create_procedure.out --- b/src/test/regress/expected/create_procedure.out *************** CALL sum(1); -- error: not a procedure *** 126,131 **** --- 126,132 ---- ERROR: sum(integer) is not a procedure LINE 1: CALL sum(1); ^ + HINT: To call a function, use SELECT. CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT INTO cp_test VALUES (1, 'a') $$; ERROR: invalid attribute in procedure definition LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT I...