From 9b46abb7c47de8aa408a8c83666fd67c5447eb85 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 31 Oct 2008 19:37:56 +0000 Subject: 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. --- src/backend/executor/execQual.c | 60 +++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 23 deletions(-) (limited to 'src/backend/executor/execQual.c') diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 4de99be6262..4af50bebd77 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -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; -- cgit v1.2.3