Skip to content

Commit 4d20bad

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 725d981 commit 4d20bad

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
@@ -80,14 +80,12 @@ static void InitPlan(QueryDesc *queryDesc, int eflags);
8080
static void CheckValidRowMarkRel(Relation rel, RowMarkType markType);
8181
static void ExecPostprocessPlan(EState *estate);
8282
static void ExecEndPlan(PlanState *planstate, EState *estate);
83-
static void ExecutePlan(EState *estate, PlanState *planstate,
84-
bool use_parallel_mode,
83+
static void ExecutePlan(QueryDesc *queryDesc,
8584
CmdType operation,
8685
bool sendTuples,
8786
uint64 numberTuples,
8887
ScanDirection direction,
89-
DestReceiver *dest,
90-
bool execute_once);
88+
DestReceiver *dest);
9189
static bool ExecCheckRTEPerms(RangeTblEntry *rte);
9290
static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid,
9391
Bitmapset *modifiedCols,
@@ -272,6 +270,9 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
272270
* retrieved tuples, not for instance to those inserted/updated/deleted
273271
* by a ModifyTable plan node.
274272
*
273+
* execute_once is ignored, and is present only to avoid an API break
274+
* in stable branches.
275+
*
275276
* There is no return value, but output tuples (if any) are sent to
276277
* the destination receiver specified in the QueryDesc; and the number
277278
* of tuples processed at the top level can be found in
@@ -342,21 +343,12 @@ standard_ExecutorRun(QueryDesc *queryDesc,
342343
* run plan
343344
*/
344345
if (!ScanDirectionIsNoMovement(direction))
345-
{
346-
if (execute_once && queryDesc->already_executed)
347-
elog(ERROR, "can't re-execute query flagged for single execution");
348-
queryDesc->already_executed = true;
349-
350-
ExecutePlan(estate,
351-
queryDesc->planstate,
352-
queryDesc->plannedstmt->parallelModeNeeded,
346+
ExecutePlan(queryDesc,
353347
operation,
354348
sendTuples,
355349
count,
356350
direction,
357-
dest,
358-
execute_once);
359-
}
351+
dest);
360352

361353
/*
362354
* shutdown tuple receiver, if we started it
@@ -1578,22 +1570,19 @@ ExecEndPlan(PlanState *planstate, EState *estate)
15781570
* moving in the specified direction.
15791571
*
15801572
* Runs to completion if numberTuples is 0
1581-
*
1582-
* Note: the ctid attribute is a 'junk' attribute that is removed before the
1583-
* user can see it
15841573
* ----------------------------------------------------------------
15851574
*/
15861575
static void
1587-
ExecutePlan(EState *estate,
1588-
PlanState *planstate,
1589-
bool use_parallel_mode,
1576+
ExecutePlan(QueryDesc *queryDesc,
15901577
CmdType operation,
15911578
bool sendTuples,
15921579
uint64 numberTuples,
15931580
ScanDirection direction,
1594-
DestReceiver *dest,
1595-
bool execute_once)
1581+
DestReceiver *dest)
15961582
{
1583+
EState *estate = queryDesc->estate;
1584+
PlanState *planstate = queryDesc->planstate;
1585+
bool use_parallel_mode;
15971586
TupleTableSlot *slot;
15981587
uint64 current_tuple_count;
15991588

@@ -1608,11 +1597,17 @@ ExecutePlan(EState *estate,
16081597
estate->es_direction = direction;
16091598

16101599
/*
1611-
* If the plan might potentially be executed multiple times, we must force
1612-
* it to run without parallelism, because we might exit early.
1600+
* Set up parallel mode if appropriate.
1601+
*
1602+
* Parallel mode only supports complete execution of a plan. If we've
1603+
* already partially executed it, or if the caller asks us to exit early,
1604+
* we must force the plan to run without parallelism.
16131605
*/
1614-
if (!execute_once)
1606+
if (queryDesc->already_executed || numberTuples != 0)
16151607
use_parallel_mode = false;
1608+
else
1609+
use_parallel_mode = queryDesc->plannedstmt->parallelModeNeeded;
1610+
queryDesc->already_executed = true;
16161611

16171612
estate->es_use_parallel_mode = use_parallel_mode;
16181613
if (use_parallel_mode)

src/backend/tcop/postgres.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ exec_simple_query(const char *query_string)
12411241
(void) PortalRun(portal,
12421242
FETCH_ALL,
12431243
true, /* always top level */
1244-
true,
1244+
true, /* ignored */
12451245
receiver,
12461246
receiver,
12471247
&qc);
@@ -2188,7 +2188,7 @@ exec_execute_message(const char *portal_name, long max_rows)
21882188
completed = PortalRun(portal,
21892189
max_rows,
21902190
true, /* always top level */
2191-
!execute_is_fetch && max_rows == FETCH_ALL,
2191+
true, /* ignored */
21922192
receiver,
21932193
receiver,
21942194
&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
}
@@ -1400,9 +1398,6 @@ PortalRunFetch(Portal portal,
14001398
*/
14011399
MarkPortalActive(portal);
14021400

1403-
/* If supporting FETCH, portal can't be run-once. */
1404-
Assert(!portal->run_once);
1405-
14061401
/*
14071402
* Set up global portal context pointers.
14081403
*/

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)