summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/pg_proc.c4
-rw-r--r--src/backend/commands/functioncmds.c65
-rw-r--r--src/backend/executor/functions.c51
-rw-r--r--src/backend/tcop/utility.c3
-rw-r--r--src/backend/utils/fmgr/funcapi.c11
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);