Skip to content

Commit 556f7b7

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 bb93b33 commit 556f7b7

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
@@ -75,14 +75,12 @@ static void InitPlan(QueryDesc *queryDesc, int eflags);
7575
static void CheckValidRowMarkRel(Relation rel, RowMarkType markType);
7676
static void ExecPostprocessPlan(EState *estate);
7777
static void ExecEndPlan(PlanState *planstate, EState *estate);
78-
static void ExecutePlan(EState *estate, PlanState *planstate,
79-
bool use_parallel_mode,
78+
static void ExecutePlan(QueryDesc *queryDesc,
8079
CmdType operation,
8180
bool sendTuples,
8281
uint64 numberTuples,
8382
ScanDirection direction,
84-
DestReceiver *dest,
85-
bool execute_once);
83+
DestReceiver *dest);
8684
static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo);
8785
static bool ExecCheckPermissionsModified(Oid relOid, Oid userid,
8886
Bitmapset *modifiedCols,
@@ -283,6 +281,9 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
283281
* retrieved tuples, not for instance to those inserted/updated/deleted
284282
* by a ModifyTable plan node.
285283
*
284+
* execute_once is ignored, and is present only to avoid an API break
285+
* in stable branches.
286+
*
286287
* There is no return value, but output tuples (if any) are sent to
287288
* the destination receiver specified in the QueryDesc; and the number
288289
* of tuples processed at the top level can be found in
@@ -357,21 +358,12 @@ standard_ExecutorRun(QueryDesc *queryDesc,
357358
* run plan
358359
*/
359360
if (!ScanDirectionIsNoMovement(direction))
360-
{
361-
if (execute_once && queryDesc->already_executed)
362-
elog(ERROR, "can't re-execute query flagged for single execution");
363-
queryDesc->already_executed = true;
364-
365-
ExecutePlan(estate,
366-
queryDesc->planstate,
367-
queryDesc->plannedstmt->parallelModeNeeded,
361+
ExecutePlan(queryDesc,
368362
operation,
369363
sendTuples,
370364
count,
371365
direction,
372-
dest,
373-
execute_once);
374-
}
366+
dest);
375367

376368
/*
377369
* Update es_total_processed to keep track of the number of tuples
@@ -1600,22 +1592,19 @@ ExecCloseRangeTableRelations(EState *estate)
16001592
* moving in the specified direction.
16011593
*
16021594
* Runs to completion if numberTuples is 0
1603-
*
1604-
* Note: the ctid attribute is a 'junk' attribute that is removed before the
1605-
* user can see it
16061595
* ----------------------------------------------------------------
16071596
*/
16081597
static void
1609-
ExecutePlan(EState *estate,
1610-
PlanState *planstate,
1611-
bool use_parallel_mode,
1598+
ExecutePlan(QueryDesc *queryDesc,
16121599
CmdType operation,
16131600
bool sendTuples,
16141601
uint64 numberTuples,
16151602
ScanDirection direction,
1616-
DestReceiver *dest,
1617-
bool execute_once)
1603+
DestReceiver *dest)
16181604
{
1605+
EState *estate = queryDesc->estate;
1606+
PlanState *planstate = queryDesc->planstate;
1607+
bool use_parallel_mode;
16191608
TupleTableSlot *slot;
16201609
uint64 current_tuple_count;
16211610

@@ -1630,11 +1619,17 @@ ExecutePlan(EState *estate,
16301619
estate->es_direction = direction;
16311620

16321621
/*
1633-
* If the plan might potentially be executed multiple times, we must force
1634-
* it to run without parallelism, because we might exit early.
1622+
* Set up parallel mode if appropriate.
1623+
*
1624+
* Parallel mode only supports complete execution of a plan. If we've
1625+
* already partially executed it, or if the caller asks us to exit early,
1626+
* we must force the plan to run without parallelism.
16351627
*/
1636-
if (!execute_once)
1628+
if (queryDesc->already_executed || numberTuples != 0)
16371629
use_parallel_mode = false;
1630+
else
1631+
use_parallel_mode = queryDesc->plannedstmt->parallelModeNeeded;
1632+
queryDesc->already_executed = true;
16381633

16391634
estate->es_use_parallel_mode = use_parallel_mode;
16401635
if (use_parallel_mode)

src/backend/tcop/postgres.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ exec_simple_query(const char *query_string)
12781278
(void) PortalRun(portal,
12791279
FETCH_ALL,
12801280
true, /* always top level */
1281-
true,
1281+
true, /* ignored */
12821282
receiver,
12831283
receiver,
12841284
&qc);
@@ -2255,7 +2255,7 @@ exec_execute_message(const char *portal_name, long max_rows)
22552255
completed = PortalRun(portal,
22562256
max_rows,
22572257
true, /* always top level */
2258-
!execute_is_fetch && max_rows == FETCH_ALL,
2258+
true, /* ignored */
22592259
receiver,
22602260
receiver,
22612261
&qc);

src/backend/tcop/pquery.c

+4-9
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
670670
* isTopLevel: true if query is being executed at backend "top level"
671671
* (that is, directly from a client command message)
672672
*
673+
* run_once: ignored, present only to avoid an API break in stable branches.
674+
*
673675
* dest: where to send output of primary (canSetTag) query
674676
*
675677
* altdest: where to send output of non-primary queries
@@ -714,10 +716,6 @@ PortalRun(Portal portal, long count, bool isTopLevel, bool run_once,
714716
*/
715717
MarkPortalActive(portal);
716718

717-
/* Set run_once flag. Shouldn't be clear if previously set. */
718-
Assert(!portal->run_once || run_once);
719-
portal->run_once = run_once;
720-
721719
/*
722720
* Set up global portal context pointers.
723721
*
@@ -922,7 +920,7 @@ PortalRunSelect(Portal portal,
922920
{
923921
PushActiveSnapshot(queryDesc->snapshot);
924922
ExecutorRun(queryDesc, direction, (uint64) count,
925-
portal->run_once);
923+
false);
926924
nprocessed = queryDesc->estate->es_processed;
927925
PopActiveSnapshot();
928926
}
@@ -962,7 +960,7 @@ PortalRunSelect(Portal portal,
962960
{
963961
PushActiveSnapshot(queryDesc->snapshot);
964962
ExecutorRun(queryDesc, direction, (uint64) count,
965-
portal->run_once);
963+
false);
966964
nprocessed = queryDesc->estate->es_processed;
967965
PopActiveSnapshot();
968966
}
@@ -1406,9 +1404,6 @@ PortalRunFetch(Portal portal,
14061404
*/
14071405
MarkPortalActive(portal);
14081406

1409-
/* If supporting FETCH, portal can't be run-once. */
1410-
Assert(!portal->run_once);
1411-
14121407
/*
14131408
* Set up global portal context pointers.
14141409
*/

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
@@ -145,7 +145,7 @@ typedef struct PortalData
145145
/* Features/options */
146146
PortalStrategy strategy; /* see above */
147147
int cursorOptions; /* DECLARE CURSOR option bits */
148-
bool run_once; /* portal will only be run once */
148+
bool run_once; /* unused */
149149

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

0 commit comments

Comments
 (0)