diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/pg_proc.c | 4 | ||||
-rw-r--r-- | src/backend/commands/functioncmds.c | 65 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 51 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 3 | ||||
-rw-r--r-- | src/backend/utils/fmgr/funcapi.c | 11 |
5 files changed, 118 insertions, 16 deletions
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 40e579f95dc..466ff038e7a 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -438,7 +438,8 @@ ProcedureCreate(const char *procedureName, TupleDesc newdesc; olddesc = build_function_result_tupdesc_t(oldtup); - newdesc = build_function_result_tupdesc_d(allParameterTypes, + newdesc = build_function_result_tupdesc_d(prokind, + allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) @@ -925,6 +926,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) querytree_sublist); } + check_sql_fn_statements(querytree_list); (void) check_sql_fn_retval(funcoid, proc->prorettype, querytree_list, NULL, NULL); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index b1f87d056e5..86fa8c0dd74 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -68,6 +68,7 @@ #include "utils/memutils.h" #include "utils/rel.h" #include "utils/syscache.h" +#include "utils/typcache.h" #include "utils/tqual.h" /* @@ -281,10 +282,11 @@ interpret_function_parameter_list(ParseState *pstate, if (objtype == OBJECT_PROCEDURE) { - if (fp->mode == FUNC_PARAM_OUT || fp->mode == FUNC_PARAM_INOUT) + if (fp->mode == FUNC_PARAM_OUT) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - (errmsg("procedures cannot have OUT parameters")))); + (errmsg("procedures cannot have OUT arguments"), + errhint("INOUT arguments are permitted.")))); } /* handle input parameters */ @@ -302,7 +304,9 @@ interpret_function_parameter_list(ParseState *pstate, /* handle output parameters */ if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC) { - if (outCount == 0) /* save first output param's type */ + if (objtype == OBJECT_PROCEDURE) + *requiredResultType = RECORDOID; + else if (outCount == 0) /* save first output param's type */ *requiredResultType = toid; outCount++; } @@ -1003,12 +1007,8 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt) if (stmt->is_procedure) { - /* - * Sometime in the future, procedures might be allowed to return - * results; for now, they all return VOID. - */ Assert(!stmt->returnType); - prorettype = VOIDOID; + prorettype = requiredResultType ? requiredResultType : VOIDOID; returnsSet = false; } else if (stmt->returnType) @@ -2206,7 +2206,7 @@ ExecuteDoStmt(DoStmt *stmt, bool atomic) * commits that might occur inside the procedure. */ void -ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic) +ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver *dest) { ListCell *lc; FuncExpr *fexpr; @@ -2219,6 +2219,7 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic) EState *estate; ExprContext *econtext; HeapTuple tp; + Datum retval; fexpr = stmt->funcexpr; Assert(fexpr); @@ -2285,7 +2286,51 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic) i++; } - FunctionCallInvoke(&fcinfo); + retval = FunctionCallInvoke(&fcinfo); + + if (fexpr->funcresulttype == VOIDOID) + { + /* do nothing */ + } + else if (fexpr->funcresulttype == RECORDOID) + { + /* + * send tuple to client + */ + + HeapTupleHeader td; + Oid tupType; + int32 tupTypmod; + TupleDesc retdesc; + HeapTupleData rettupdata; + TupOutputState *tstate; + TupleTableSlot *slot; + + if (fcinfo.isnull) + elog(ERROR, "procedure returned null record"); + + td = DatumGetHeapTupleHeader(retval); + tupType = HeapTupleHeaderGetTypeId(td); + tupTypmod = HeapTupleHeaderGetTypMod(td); + retdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + + tstate = begin_tup_output_tupdesc(dest, retdesc); + + rettupdata.t_len = HeapTupleHeaderGetDatumLength(td); + ItemPointerSetInvalid(&(rettupdata.t_self)); + rettupdata.t_tableOid = InvalidOid; + rettupdata.t_data = td; + + slot = ExecStoreTuple(&rettupdata, tstate->slot, InvalidBuffer, false); + tstate->dest->receiveSlot(slot, tstate->dest); + + end_tup_output(tstate); + + ReleaseTupleDesc(retdesc); + } + else + elog(ERROR, "unexpected result type for procedure: %u", + fexpr->funcresulttype); FreeExecutorState(estate); } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 78bc4ab34bd..1c00ac9588f 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -721,6 +721,8 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK) list_copy(queryTree_sublist)); } + check_sql_fn_statements(flat_query_list); + /* * Check that the function returns the type it claims to. Although in * simple cases this was already done when the function was defined, we @@ -1486,6 +1488,55 @@ ShutdownSQLFunction(Datum arg) fcache->shutdown_reg = false; } +/* + * check_sql_fn_statements + * + * Check statements in an SQL function. Error out if there is anything that + * is not acceptable. + */ +void +check_sql_fn_statements(List *queryTreeList) +{ + ListCell *lc; + + foreach(lc, queryTreeList) + { + Query *query = lfirst_node(Query, lc); + + /* + * Disallow procedures with output arguments. The current + * implementation would just throw the output values away, unless the + * statement is the last one. Per SQL standard, we should assign the + * output values by name. By disallowing this here, we preserve an + * opportunity for future improvement. + */ + if (query->commandType == CMD_UTILITY && + IsA(query->utilityStmt, CallStmt)) + { + CallStmt *stmt = castNode(CallStmt, query->utilityStmt); + HeapTuple tuple; + int numargs; + Oid *argtypes; + char **argnames; + char *argmodes; + int i; + + tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(stmt->funcexpr->funcid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", stmt->funcexpr->funcid); + numargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes); + ReleaseSysCache(tuple); + + for (i = 0; i < numargs; i++) + { + if (argmodes && (argmodes[i] == PROARGMODE_INOUT || argmodes[i] == PROARGMODE_OUT)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("calling procedures with output arguments is not supported in SQL functions"))); + } + } + } +} /* * check_sql_fn_retval() -- check return value of a list of sql parse trees. diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index f78efdf359a..6effe031f85 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -661,7 +661,8 @@ standard_ProcessUtility(PlannedStmt *pstmt, case T_CallStmt: ExecuteCallStmt(castNode(CallStmt, parsetree), params, - (context != PROCESS_UTILITY_TOPLEVEL || IsTransactionBlock())); + (context != PROCESS_UTILITY_TOPLEVEL || IsTransactionBlock()), + dest); break; case T_ClusterStmt: diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index c0076bfce3f..20f60392afe 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -1205,7 +1205,8 @@ build_function_result_tupdesc_t(HeapTuple procTuple) if (isnull) proargnames = PointerGetDatum(NULL); /* just to be sure */ - return build_function_result_tupdesc_d(proallargtypes, + return build_function_result_tupdesc_d(procform->prokind, + proallargtypes, proargmodes, proargnames); } @@ -1218,10 +1219,12 @@ build_function_result_tupdesc_t(HeapTuple procTuple) * convenience of ProcedureCreate, which needs to be able to compute the * tupledesc before actually creating the function. * - * Returns NULL if there are not at least two OUT or INOUT arguments. + * For functions (but not for procedures), returns NULL if there are not at + * least two OUT or INOUT arguments. */ TupleDesc -build_function_result_tupdesc_d(Datum proallargtypes, +build_function_result_tupdesc_d(char prokind, + Datum proallargtypes, Datum proargmodes, Datum proargnames) { @@ -1311,7 +1314,7 @@ build_function_result_tupdesc_d(Datum proallargtypes, * If there is no output argument, or only one, the function does not * return tuples. */ - if (numoutargs < 2) + if (numoutargs < 2 && prokind != PROKIND_PROCEDURE) return NULL; desc = CreateTemplateTupleDesc(numoutargs, false); |