]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Make ExecForPortionOfLeftovers() obey SRF protocol.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 20 Apr 2026 14:21:52 +0000 (10:21 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 20 Apr 2026 14:21:52 +0000 (10:21 -0400)
Before each call to the SRF, initialize isnull and isDone, as per the
comments for struct ReturnSetInfo.  This fixes a Coverity warning
about rsi.isDone not being initialized.  The built-in
{multi,}range_minus_multi functions don't return without setting it,
but a user-supplied function might not be as accommodating.

We also add statistics tracking around the function call, which
will be expected once user-defined withoutPortionProcs functions
are supported, and a cross-check on rsi.returnMode just for
paranoia's sake.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Co-authored-by: Paul A Jungwirth <pj@illuminatedcomputing.com>
Discussion: https://postgr.es/m/4126231.1776622202@sss.pgh.pa.us

src/backend/executor/nodeModifyTable.c

index ef2a6bc6e9d11dee0c4d0fa2c6f92fdf012c99ec..e8a044f56c61e3440f74f112090acc60431d9fab 100644 (file)
@@ -65,6 +65,7 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/optimizer.h"
+#include "pgstat.h"
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
 #include "storage/lmgr.h"
@@ -1419,6 +1420,7 @@ ExecForPortionOfLeftovers(ModifyTableContext *context,
        CmdType         oldOperation;
        TransitionCaptureState *oldTcs;
        FmgrInfo        flinfo;
+       PgStat_FunctionCallUsage fcusage;
        ReturnSetInfo rsi;
        bool            didInit = false;
        bool            shouldFree = false;
@@ -1514,6 +1516,7 @@ ExecForPortionOfLeftovers(ModifyTableContext *context,
        rsi.expectedDesc = NULL;
        rsi.allowedModes = (int) (SFRM_ValuePerCall);
        rsi.returnMode = SFRM_ValuePerCall;
+       /* isDone is filled below */
        rsi.setResult = NULL;
        rsi.setDesc = NULL;
 
@@ -1537,14 +1540,27 @@ ExecForPortionOfLeftovers(ModifyTableContext *context,
         */
        while (true)
        {
-               Datum           leftover = FunctionCallInvoke(fcinfo);
+               Datum           leftover;
+
+               /* Call the function one time */
+               pgstat_init_function_usage(fcinfo, &fcusage);
+
+               fcinfo->isnull = false;
+               rsi.isDone = ExprSingleResult;
+               leftover = FunctionCallInvoke(fcinfo);
+
+               pgstat_end_function_usage(&fcusage,
+                                                                 rsi.isDone != ExprMultipleResult);
+
+               if (rsi.returnMode != SFRM_ValuePerCall)
+                       elog(ERROR, "without_portion function violated function call protocol");
 
                /* Are we done? */
                if (rsi.isDone == ExprEndResult)
                        break;
 
                if (fcinfo->isnull)
-                       elog(ERROR, "Got a null from without_portion function");
+                       elog(ERROR, "got a null from without_portion function");
 
                /*
                 * Does the new Datum violate domain checks? Row-level CHECK