Skip to content

Commit 8a95ad3

Browse files
committed
Simplify executor's determination of whether to use parallelism.
Our parallel-mode code only works when we are executing a query in full, so ExecutePlan must disable parallel mode when it is asked to do partial execution. The previous logic for this involved passing down a flag (variously named execute_once or run_once) from callers of ExecutorRun or PortalRun. This is overcomplicated, and unsurprisingly some of the callers didn't get it right, since it requires keeping state that not all of them have handy; not to mention that the requirements for it were undocumented. That led to assertion failures in some corner cases. The only state we really need for this is the existing QueryDesc.already_executed flag, so let's just put all the responsibility in ExecutePlan. (It could have been done in ExecutorRun too, leading to a slightly shorter patch -- but if there's ever more than one caller of ExecutePlan, it seems better to have this logic in the subroutine than the callers.) This makes those ExecutorRun/PortalRun parameters unnecessary. In master it seems okay to just remove them, returning the API for those functions to what it was before parallelism. Such an API break is clearly not okay in stable branches, but for them we can just leave the parameters in place after documenting that they do nothing. Per report from Yugo Nagata, who also reviewed and tested this patch. Back-patch to all supported branches. Discussion: https://postgr.es/m/20241206062549.710dc01cf91224809dd6c0e1@sraoss.co.jp
1 parent 1a34cf0 commit 8a95ad3

File tree

5 files changed

+29
-39
lines changed

5 files changed

+29
-39
lines changed

src/backend/executor/execMain.c

+21-26
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,12 @@ static void InitPlan(QueryDesc *queryDesc, int eflags);
8282
static void CheckValidRowMarkRel(Relation rel, RowMarkType markType);
8383
static void ExecPostprocessPlan(EState *estate);
8484
static void ExecEndPlan(PlanState *planstate, EState *estate);
85-
static void ExecutePlan(EState *estate, PlanState *planstate,
86-
bool use_parallel_mode,
85+
static void ExecutePlan(QueryDesc *queryDesc,
8786
CmdType operation,
8887
bool sendTuples,
8988
uint64 numberTuples,
9089
ScanDirection direction,
91-
DestReceiver *dest,
92-
bool execute_once);
90+
DestReceiver *dest);
9391
static bool ExecCheckRTEPerms(RangeTblEntry *rte);
9492
static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid,
9593
Bitmapset *modifiedCols,
@@ -286,6 +284,9 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
286284
* retrieved tuples, not for instance to those inserted/updated/deleted
287285
* by a ModifyTable plan node.
288286
*
287+
* execute_once is ignored, and is present only to avoid an API break
288+
* in stable branches.
289+
*
289290
* There is no return value, but output tuples (if any) are sent to
290291
* the destination receiver specified in the QueryDesc; and the number
291292
* of tuples processed at the top level can be found in
@@ -356,21 +357,12 @@ standard_ExecutorRun(QueryDesc *queryDesc,
356357
* run plan
357358
*/
358359
if (!ScanDirectionIsNoMovement(direction))
359-
{
360-
if (execute_once && queryDesc->already_executed)
361-
elog(ERROR, "can't re-execute query flagged for single execution");
362-
queryDesc->already_executed = true;
363-
364-
ExecutePlan(estate,
365-
queryDesc->planstate,
366-
queryDesc->plannedstmt->parallelModeNeeded,
360+
ExecutePlan(queryDesc,
367361
operation,
368362
sendTuples,
369363
count,
370364
direction,
371-
dest,
372-
execute_once);
373-
}
365+
dest);
374366

375367
/*
376368
* shutdown tuple receiver, if we started it
@@ -1506,22 +1498,19 @@ ExecCloseRangeTableRelations(EState *estate)
15061498
* moving in the specified direction.
15071499
*
15081500
* Runs to completion if numberTuples is 0
1509-
*
1510-
* Note: the ctid attribute is a 'junk' attribute that is removed before the
1511-
* user can see it
15121501
* ----------------------------------------------------------------
15131502
*/
15141503
static void
1515-
ExecutePlan(EState *estate,
1516-
PlanState *planstate,
1517-
bool use_parallel_mode,
1504+
ExecutePlan(QueryDesc *queryDesc,
15181505
CmdType operation,
15191506
bool sendTuples,
15201507
uint64 numberTuples,
15211508
ScanDirection direction,
1522-
DestReceiver *dest,
1523-
bool execute_once)
1509+
DestReceiver *dest)
15241510
{
1511+
EState *estate = queryDesc->estate;
1512+
PlanState *planstate = queryDesc->planstate;
1513+
bool use_parallel_mode;
15251514
TupleTableSlot *slot;
15261515
uint64 current_tuple_count;
15271516

@@ -1536,11 +1525,17 @@ ExecutePlan(EState *estate,
15361525
estate->es_direction = direction;
15371526

15381527
/*
1539-
* If the plan might potentially be executed multiple times, we must force
1540-
* it to run without parallelism, because we might exit early.
1528+
* Set up parallel mode if appropriate.
1529+
*
1530+
* Parallel mode only supports complete execution of a plan. If we've
1531+
* already partially executed it, or if the caller asks us to exit early,
1532+
* we must force the plan to run without parallelism.
15411533
*/
1542-
if (!execute_once)
1534+
if (queryDesc->already_executed || numberTuples != 0)
15431535
use_parallel_mode = false;
1536+
else
1537+
use_parallel_mode = queryDesc->plannedstmt->parallelModeNeeded;
1538+
queryDesc->already_executed = true;
15441539

15451540
estate->es_use_parallel_mode = use_parallel_mode;
15461541
if (use_parallel_mode)

src/backend/tcop/postgres.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ exec_simple_query(const char *query_string)
12171217
(void) PortalRun(portal,
12181218
FETCH_ALL,
12191219
true, /* always top level */
1220-
true,
1220+
true, /* ignored */
12211221
receiver,
12221222
receiver,
12231223
&qc);
@@ -2215,7 +2215,7 @@ exec_execute_message(const char *portal_name, long max_rows)
22152215
completed = PortalRun(portal,
22162216
max_rows,
22172217
true, /* always top level */
2218-
!execute_is_fetch && max_rows == FETCH_ALL,
2218+
true, /* ignored */
22192219
receiver,
22202220
receiver,
22212221
&qc);

src/backend/tcop/pquery.c

+4-9
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
667667
* isTopLevel: true if query is being executed at backend "top level"
668668
* (that is, directly from a client command message)
669669
*
670+
* run_once: ignored, present only to avoid an API break in stable branches.
671+
*
670672
* dest: where to send output of primary (canSetTag) query
671673
*
672674
* altdest: where to send output of non-primary queries
@@ -711,10 +713,6 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
711713
*/
712714
MarkPortalActive(portal);
713715

714-
/* Set run_once flag. Shouldn't be clear if previously set. */
715-
Assert(!portal->run_once || run_once);
716-
portal->run_once = run_once;
717-
718716
/*
719717
* Set up global portal context pointers.
720718
*
@@ -919,7 +917,7 @@ PortalRunSelect(Portal portal,
919917
{
920918
PushActiveSnapshot(queryDesc->snapshot);
921919
ExecutorRun(queryDesc, direction, (uint64) count,
922-
portal->run_once);
920+
false);
923921
nprocessed = queryDesc->estate->es_processed;
924922
PopActiveSnapshot();
925923
}
@@ -959,7 +957,7 @@ PortalRunSelect(Portal portal,
959957
{
960958
PushActiveSnapshot(queryDesc->snapshot);
961959
ExecutorRun(queryDesc, direction, (uint64) count,
962-
portal->run_once);
960+
false);
963961
nprocessed = queryDesc->estate->es_processed;
964962
PopActiveSnapshot();
965963
}
@@ -1403,9 +1401,6 @@ PortalRunFetch(Portal portal,
14031401
*/
14041402
MarkPortalActive(portal);
14051403

1406-
/* If supporting FETCH, portal can't be run-once. */
1407-
Assert(!portal->run_once);
1408-
14091404
/*
14101405
* Set up global portal context pointers.
14111406
*/

src/include/executor/execdesc.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ typedef struct QueryDesc
4848
EState *estate; /* executor's query-wide state */
4949
PlanState *planstate; /* tree of per-plan-node state */
5050

51-
/* This field is set by ExecutorRun */
51+
/* This field is set by ExecutePlan */
5252
bool already_executed; /* true if previously executed */
5353

5454
/* This is always set NULL by the core system, but plugins can change it */

src/include/utils/portal.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ typedef struct PortalData
144144
/* Features/options */
145145
PortalStrategy strategy; /* see above */
146146
int cursorOptions; /* DECLARE CURSOR option bits */
147-
bool run_once; /* portal will only be run once */
147+
bool run_once; /* unused */
148148

149149
/* Status data */
150150
PortalStatus status; /* see above */

0 commit comments

Comments
 (0)