mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
Allow SQL-language functions to return the output of an INSERT/UPDATE/DELETE
RETURNING clause, not just a SELECT as formerly. A side effect of this patch is that when a set-returning SQL function is used in a FROM clause, performance is improved because the output is collected into a tuplestore within the function, rather than using the less efficient value-per-call mechanism.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.235 2008/10/29 00:00:38 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.236 2008/10/31 19:37:56 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1334,7 +1334,8 @@ ExecMakeFunctionResult(FuncExprState *fcache,
|
||||
{
|
||||
List *arguments;
|
||||
Datum result;
|
||||
FunctionCallInfoData fcinfo;
|
||||
FunctionCallInfoData fcinfo_data;
|
||||
FunctionCallInfo fcinfo;
|
||||
PgStat_FunctionCallUsage fcusage;
|
||||
ReturnSetInfo rsinfo; /* for functions returning sets */
|
||||
ExprDoneCond argDone;
|
||||
@ -1384,6 +1385,20 @@ restart:
|
||||
Assert(!fcache->setArgsValid);
|
||||
}
|
||||
|
||||
/*
|
||||
* For non-set-returning functions, we just use a local-variable
|
||||
* FunctionCallInfoData. For set-returning functions we keep the callinfo
|
||||
* record in fcache->setArgs so that it can survive across multiple
|
||||
* value-per-call invocations. (The reason we don't just do the latter
|
||||
* all the time is that plpgsql expects to be able to use simple expression
|
||||
* trees re-entrantly. Which might not be a good idea, but the penalty
|
||||
* for not doing so is high.)
|
||||
*/
|
||||
if (fcache->func.fn_retset)
|
||||
fcinfo = &fcache->setArgs;
|
||||
else
|
||||
fcinfo = &fcinfo_data;
|
||||
|
||||
/*
|
||||
* arguments is a list of expressions to evaluate before passing to the
|
||||
* function manager. We skip the evaluation if it was already done in the
|
||||
@ -1394,8 +1409,8 @@ restart:
|
||||
if (!fcache->setArgsValid)
|
||||
{
|
||||
/* Need to prep callinfo structure */
|
||||
InitFunctionCallInfoData(fcinfo, &(fcache->func), 0, NULL, NULL);
|
||||
argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
|
||||
InitFunctionCallInfoData(*fcinfo, &(fcache->func), 0, NULL, NULL);
|
||||
argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext);
|
||||
if (argDone == ExprEndResult)
|
||||
{
|
||||
/* input is an empty set, so return an empty set. */
|
||||
@ -1412,8 +1427,7 @@ restart:
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Copy callinfo from previous evaluation */
|
||||
memcpy(&fcinfo, &fcache->setArgs, sizeof(fcinfo));
|
||||
/* Re-use callinfo from previous evaluation */
|
||||
hasSetArg = fcache->setHasSetArg;
|
||||
/* Reset flag (we may set it again below) */
|
||||
fcache->setArgsValid = false;
|
||||
@ -1424,12 +1438,12 @@ restart:
|
||||
*/
|
||||
if (fcache->func.fn_retset)
|
||||
{
|
||||
fcinfo.resultinfo = (Node *) &rsinfo;
|
||||
fcinfo->resultinfo = (Node *) &rsinfo;
|
||||
rsinfo.type = T_ReturnSetInfo;
|
||||
rsinfo.econtext = econtext;
|
||||
rsinfo.expectedDesc = fcache->funcResultDesc;
|
||||
rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
|
||||
/* note we do not set SFRM_Materialize_Random */
|
||||
/* note we do not set SFRM_Materialize_Random or _Preferred */
|
||||
rsinfo.returnMode = SFRM_ValuePerCall;
|
||||
/* isDone is filled below */
|
||||
rsinfo.setResult = NULL;
|
||||
@ -1468,9 +1482,9 @@ restart:
|
||||
|
||||
if (fcache->func.fn_strict)
|
||||
{
|
||||
for (i = 0; i < fcinfo.nargs; i++)
|
||||
for (i = 0; i < fcinfo->nargs; i++)
|
||||
{
|
||||
if (fcinfo.argnull[i])
|
||||
if (fcinfo->argnull[i])
|
||||
{
|
||||
callit = false;
|
||||
break;
|
||||
@ -1480,12 +1494,12 @@ restart:
|
||||
|
||||
if (callit)
|
||||
{
|
||||
pgstat_init_function_usage(&fcinfo, &fcusage);
|
||||
pgstat_init_function_usage(fcinfo, &fcusage);
|
||||
|
||||
fcinfo.isnull = false;
|
||||
fcinfo->isnull = false;
|
||||
rsinfo.isDone = ExprSingleResult;
|
||||
result = FunctionCallInvoke(&fcinfo);
|
||||
*isNull = fcinfo.isnull;
|
||||
result = FunctionCallInvoke(fcinfo);
|
||||
*isNull = fcinfo->isnull;
|
||||
*isDone = rsinfo.isDone;
|
||||
|
||||
pgstat_end_function_usage(&fcusage,
|
||||
@ -1511,7 +1525,7 @@ restart:
|
||||
if (fcache->func.fn_retset &&
|
||||
*isDone == ExprMultipleResult)
|
||||
{
|
||||
memcpy(&fcache->setArgs, &fcinfo, sizeof(fcinfo));
|
||||
Assert(fcinfo == &fcache->setArgs);
|
||||
fcache->setHasSetArg = hasSetArg;
|
||||
fcache->setArgsValid = true;
|
||||
/* Register cleanup callback if we didn't already */
|
||||
@ -1567,7 +1581,7 @@ restart:
|
||||
break; /* input not a set, so done */
|
||||
|
||||
/* Re-eval args to get the next element of the input set */
|
||||
argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
|
||||
argDone = ExecEvalFuncArgs(fcinfo, arguments, econtext);
|
||||
|
||||
if (argDone != ExprMultipleResult)
|
||||
{
|
||||
@ -1605,9 +1619,9 @@ restart:
|
||||
*/
|
||||
if (fcache->func.fn_strict)
|
||||
{
|
||||
for (i = 0; i < fcinfo.nargs; i++)
|
||||
for (i = 0; i < fcinfo->nargs; i++)
|
||||
{
|
||||
if (fcinfo.argnull[i])
|
||||
if (fcinfo->argnull[i])
|
||||
{
|
||||
*isNull = true;
|
||||
return (Datum) 0;
|
||||
@ -1615,11 +1629,11 @@ restart:
|
||||
}
|
||||
}
|
||||
|
||||
pgstat_init_function_usage(&fcinfo, &fcusage);
|
||||
pgstat_init_function_usage(fcinfo, &fcusage);
|
||||
|
||||
fcinfo.isnull = false;
|
||||
result = FunctionCallInvoke(&fcinfo);
|
||||
*isNull = fcinfo.isnull;
|
||||
fcinfo->isnull = false;
|
||||
result = FunctionCallInvoke(fcinfo);
|
||||
*isNull = fcinfo->isnull;
|
||||
|
||||
pgstat_end_function_usage(&fcusage, true);
|
||||
}
|
||||
@ -1737,7 +1751,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
|
||||
rsinfo.type = T_ReturnSetInfo;
|
||||
rsinfo.econtext = econtext;
|
||||
rsinfo.expectedDesc = expectedDesc;
|
||||
rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
|
||||
rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
|
||||
if (randomAccess)
|
||||
rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
|
||||
rsinfo.returnMode = SFRM_ValuePerCall;
|
||||
|
Reference in New Issue
Block a user