Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit70b42f2

Browse files
committed
Fix misbehavior of EvalPlanQual checks with multiple result relations.
The idea of EvalPlanQual is that we replace the query's scan of theresult relation with a single injected tuple, and see if we get atuple out, thereby implying that the injected tuple still passes thequery quals. (In join cases, other relations in the query are stillscanned normally.) This logic was not updated when commit86dc900made it possible for a single DML query plan to have multiple resultrelations, when the query target relation has inheritance or partitionchildren. We replaced the output for the current result relationsuccessfully, but other result relations were still scanned normally;thus, if any other result relation contained a tuple satisfying thequals, we'd think the EPQ check passed, even if it did not pass forthe injected tuple itself. This would lead to update or deleteactions getting performed when they should have been skipped due toa conflicting concurrent update in READ COMMITTED isolation mode.Fix by blocking all sibling result relations from emitting tuplesduring an EvalPlanQual recheck. In the back branches, the fix iscomplicated a bit by the need to not change the size of structEPQState (else we'd have ABI-breaking changes in offsets instruct ModifyTableState). Like the back-patches of3f7836fand4b3e379, add a separately palloc'd struct to avoid that.The logic is the same as in HEAD otherwise.This is only a live bug back to v14 where86dc900 came in.However, I chose to back-patch the test cases further, on thegrounds that this whole area is none too well tested. I skippeddoing so in v11 though because none of the test applied cleanly,and it didn't quite seem worth extra work for a branch with onlysix months to live.Per report from Ante Krešić (via Aleksander Alekseev)Discussion:https://postgr.es/m/CAJ7c6TMBTN3rcz4=AjYhLPD_w3FFT0Wq_C15jxCDn8U4tZnH1g@mail.gmail.com
1 parented7e686 commit70b42f2

File tree

9 files changed

+221
-48
lines changed

9 files changed

+221
-48
lines changed

‎src/backend/executor/execMain.c

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,7 +2469,7 @@ ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
24692469
*relation - table containing tuple
24702470
*rti - rangetable index of table containing tuple
24712471
*inputslot - tuple for processing - this can be the slot from
2472-
*EvalPlanQualSlot(), forthe increased efficiency.
2472+
*EvalPlanQualSlot() forthis rel, for increased efficiency.
24732473
*
24742474
* This tests whether the tuple in inputslot still matches the relevant
24752475
* quals. For that result to be useful, typically the input tuple has to be
@@ -2503,6 +2503,14 @@ EvalPlanQual(EPQState *epqstate, Relation relation,
25032503
if (testslot!=inputslot)
25042504
ExecCopySlot(testslot,inputslot);
25052505

2506+
/*
2507+
* Mark that an EPQ tuple is available for this relation. (If there is
2508+
* more than one result relation, the others remain marked as having no
2509+
* tuple available.)
2510+
*/
2511+
epqstate->relsubs_done[rti-1]= false;
2512+
epqstate->relsubs_blocked[rti-1]= false;
2513+
25062514
/*
25072515
* Run the EPQ query. We assume it will return at most one tuple.
25082516
*/
@@ -2519,11 +2527,12 @@ EvalPlanQual(EPQState *epqstate, Relation relation,
25192527
ExecMaterializeSlot(slot);
25202528

25212529
/*
2522-
* Clear out the test tuple. This is needed in case the EPQ query is
2523-
* re-used to test a tuple for a different relation. (Not clear that can
2524-
*really happen, but let's be safe.)
2530+
* Clear out the test tuple, and mark that no tuple is available here.
2531+
*This is needed in case the EPQ state isre-used to test a tuple for a
2532+
*different target relation.
25252533
*/
25262534
ExecClearTuple(testslot);
2535+
epqstate->relsubs_blocked[rti-1]= true;
25272536

25282537
returnslot;
25292538
}
@@ -2532,18 +2541,26 @@ EvalPlanQual(EPQState *epqstate, Relation relation,
25322541
* EvalPlanQualInit -- initialize during creation of a plan state node
25332542
* that might need to invoke EPQ processing.
25342543
*
2544+
* If the caller intends to use EvalPlanQual(), resultRelations should be
2545+
* a list of RT indexes of potential target relations for EvalPlanQual(),
2546+
* and we will arrange that the other listed relations don't return any
2547+
* tuple during an EvalPlanQual() call. Otherwise resultRelations
2548+
* should be NIL.
2549+
*
25352550
* Note: subplan/auxrowmarks can be NULL/NIL if they will be set later
25362551
* with EvalPlanQualSetPlan.
25372552
*/
25382553
void
25392554
EvalPlanQualInit(EPQState*epqstate,EState*parentestate,
2540-
Plan*subplan,List*auxrowmarks,intepqParam)
2555+
Plan*subplan,List*auxrowmarks,
2556+
intepqParam,List*resultRelations)
25412557
{
25422558
Indexrtsize=parentestate->es_range_table_size;
25432559

25442560
/* initialize data not changing over EPQState's lifetime */
25452561
epqstate->parentestate=parentestate;
25462562
epqstate->epqParam=epqParam;
2563+
epqstate->resultRelations=resultRelations;
25472564

25482565
/*
25492566
* Allocate space to reference a slot for each potential rti - do so now
@@ -2566,6 +2583,7 @@ EvalPlanQualInit(EPQState *epqstate, EState *parentestate,
25662583
epqstate->recheckplanstate=NULL;
25672584
epqstate->relsubs_rowmark=NULL;
25682585
epqstate->relsubs_done=NULL;
2586+
epqstate->relsubs_blocked=NULL;
25692587
}
25702588

25712589
/*
@@ -2763,7 +2781,13 @@ EvalPlanQualBegin(EPQState *epqstate)
27632781
Indexrtsize=parentestate->es_range_table_size;
27642782
PlanState*rcplanstate=epqstate->recheckplanstate;
27652783

2766-
MemSet(epqstate->relsubs_done,0,rtsize*sizeof(bool));
2784+
/*
2785+
* Reset the relsubs_done[] flags to equal relsubs_blocked[], so that
2786+
* the EPQ run will never attempt to fetch tuples from blocked target
2787+
* relations.
2788+
*/
2789+
memcpy(epqstate->relsubs_done,epqstate->relsubs_blocked,
2790+
rtsize*sizeof(bool));
27672791

27682792
/* Recopy current values of parent parameters */
27692793
if (parentestate->es_plannedstmt->paramExecTypes!=NIL)
@@ -2931,10 +2955,22 @@ EvalPlanQualStart(EPQState *epqstate, Plan *planTree)
29312955
}
29322956

29332957
/*
2934-
* Initialize per-relation EPQ tuple states to not-fetched.
2958+
* Initialize per-relation EPQ tuple states. Result relations, if any,
2959+
* get marked as blocked; others as not-fetched.
29352960
*/
2936-
epqstate->relsubs_done= (bool*)
2937-
palloc0(rtsize*sizeof(bool));
2961+
epqstate->relsubs_done=palloc_array(bool,rtsize);
2962+
epqstate->relsubs_blocked=palloc0_array(bool,rtsize);
2963+
2964+
foreach(l,epqstate->resultRelations)
2965+
{
2966+
intrtindex=lfirst_int(l);
2967+
2968+
Assert(rtindex>0&&rtindex <=rtsize);
2969+
epqstate->relsubs_blocked[rtindex-1]= true;
2970+
}
2971+
2972+
memcpy(epqstate->relsubs_done,epqstate->relsubs_blocked,
2973+
rtsize*sizeof(bool));
29382974

29392975
/*
29402976
* Initialize the private state information for all the nodes in the part
@@ -3010,4 +3046,5 @@ EvalPlanQualEnd(EPQState *epqstate)
30103046
epqstate->recheckplanstate=NULL;
30113047
epqstate->relsubs_rowmark=NULL;
30123048
epqstate->relsubs_done=NULL;
3049+
epqstate->relsubs_blocked=NULL;
30133050
}

‎src/backend/executor/execScan.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,12 @@ ExecScanFetch(ScanState *node,
6969
elseif (epqstate->relsubs_done[scanrelid-1])
7070
{
7171
/*
72-
* Return empty slot, aswe already performed an EPQsubstitution
73-
*for this relation.
72+
* Return empty slot, aseither there is no EPQtuple for this rel
73+
*or we already returned it.
7474
*/
7575

7676
TupleTableSlot*slot=node->ss_ScanTupleSlot;
7777

78-
/* Return empty slot, as we already returned a tuple */
7978
returnExecClearTuple(slot);
8079
}
8180
elseif (epqstate->relsubs_slot[scanrelid-1]!=NULL)
@@ -88,7 +87,7 @@ ExecScanFetch(ScanState *node,
8887

8988
Assert(epqstate->relsubs_rowmark[scanrelid-1]==NULL);
9089

91-
/* Mark to remember that we shouldn't returnmore */
90+
/* Mark to remember that we shouldn't returnit again */
9291
epqstate->relsubs_done[scanrelid-1]= true;
9392

9493
/* Return empty slot if we haven't got a test tuple */
@@ -306,14 +305,18 @@ ExecScanReScan(ScanState *node)
306305
*/
307306
ExecClearTuple(node->ss_ScanTupleSlot);
308307

309-
/* Rescan EvalPlanQual tuple if we're inside an EvalPlanQual recheck */
308+
/*
309+
* Rescan EvalPlanQual tuple(s) if we're inside an EvalPlanQual recheck.
310+
* But don't lose the "blocked" status of blocked target relations.
311+
*/
310312
if (estate->es_epq_active!=NULL)
311313
{
312314
EPQState*epqstate=estate->es_epq_active;
313315
Indexscanrelid= ((Scan*)node->ps.plan)->scanrelid;
314316

315317
if (scanrelid>0)
316-
epqstate->relsubs_done[scanrelid-1]= false;
318+
epqstate->relsubs_done[scanrelid-1]=
319+
epqstate->relsubs_blocked[scanrelid-1];
317320
else
318321
{
319322
Bitmapset*relids;
@@ -335,7 +338,8 @@ ExecScanReScan(ScanState *node)
335338
while ((rtindex=bms_next_member(relids,rtindex)) >=0)
336339
{
337340
Assert(rtindex>0);
338-
epqstate->relsubs_done[rtindex-1]= false;
341+
epqstate->relsubs_done[rtindex-1]=
342+
epqstate->relsubs_blocked[rtindex-1];
339343
}
340344
}
341345
}

‎src/backend/executor/nodeLockRows.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ ExecLockRows(PlanState *pstate)
108108
/* this child is inactive right now */
109109
erm->ermActive= false;
110110
ItemPointerSetInvalid(&(erm->curCtid));
111-
ExecClearTuple(markSlot);
112111
continue;
113112
}
114113
}
@@ -370,7 +369,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
370369

371370
/* Now we have the info needed to set up EPQ state */
372371
EvalPlanQualInit(&lrstate->lr_epqstate,estate,
373-
outerPlan,epq_arowmarks,node->epqParam);
372+
outerPlan,epq_arowmarks,node->epqParam,NIL);
374373

375374
returnlrstate;
376375
}

‎src/backend/executor/nodeModifyTable.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3985,7 +3985,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
39853985
}
39863986

39873987
/* set up epqstate with dummy subplan data for the moment */
3988-
EvalPlanQualInit(&mtstate->mt_epqstate,estate,NULL,NIL,node->epqParam);
3988+
EvalPlanQualInit(&mtstate->mt_epqstate,estate,NULL,NIL,
3989+
node->epqParam,node->resultRelations);
39893990
mtstate->fireBSTriggers= true;
39903991

39913992
/*

‎src/backend/replication/logical/worker.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,7 +2674,7 @@ apply_handle_update_internal(ApplyExecutionData *edata,
26742674
boolfound;
26752675
MemoryContextoldctx;
26762676

2677-
EvalPlanQualInit(&epqstate,estate,NULL,NIL,-1);
2677+
EvalPlanQualInit(&epqstate,estate,NULL,NIL,-1,NIL);
26782678
ExecOpenIndices(relinfo, false);
26792679

26802680
found=FindReplTupleInLocalRel(estate,localrel,
@@ -2827,7 +2827,7 @@ apply_handle_delete_internal(ApplyExecutionData *edata,
28272827
TupleTableSlot*localslot;
28282828
boolfound;
28292829

2830-
EvalPlanQualInit(&epqstate,estate,NULL,NIL,-1);
2830+
EvalPlanQualInit(&epqstate,estate,NULL,NIL,-1,NIL);
28312831
ExecOpenIndices(relinfo, false);
28322832

28332833
found=FindReplTupleInLocalRel(estate,localrel,remoterel,localindexoid,
@@ -3054,7 +3054,7 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
30543054
*/
30553055
EPQStateepqstate;
30563056

3057-
EvalPlanQualInit(&epqstate,estate,NULL,NIL,-1);
3057+
EvalPlanQualInit(&epqstate,estate,NULL,NIL,-1,NIL);
30583058
ExecOpenIndices(partrelinfo, false);
30593059

30603060
EvalPlanQualSetSlot(&epqstate,remoteslot_part);

‎src/include/executor/executor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
233233
externTupleTableSlot*EvalPlanQual(EPQState*epqstate,Relationrelation,
234234
Indexrti,TupleTableSlot*inputslot);
235235
externvoidEvalPlanQualInit(EPQState*epqstate,EState*parentestate,
236-
Plan*subplan,List*auxrowmarks,intepqParam);
236+
Plan*subplan,List*auxrowmarks,
237+
intepqParam,List*resultRelations);
237238
externvoidEvalPlanQualSetPlan(EPQState*epqstate,
238239
Plan*subplan,List*auxrowmarks);
239240
externTupleTableSlot*EvalPlanQualSlot(EPQState*epqstate,

‎src/include/nodes/execnodes.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,15 +1169,16 @@ typedef struct PlanState
11691169
*/
11701170
typedefstructEPQState
11711171
{
1172-
/* Initialized at EvalPlanQualInit() time: */
1173-
1172+
/* These are initialized by EvalPlanQualInit() and do not change later: */
11741173
EState*parentestate;/* main query's EState */
11751174
intepqParam;/* ID of Param to force scan node re-eval */
1175+
List*resultRelations;/* integer list of RT indexes, or NIL */
11761176

11771177
/*
1178-
* Tuples to be substituted by scan nodes. They need to set up, before
1179-
* calling EvalPlanQual()/EvalPlanQualNext(), into the slot returned by
1180-
* EvalPlanQualSlot(scanrelid). The array is indexed by scanrelid - 1.
1178+
* relsubs_slot[scanrelid - 1] holds the EPQ test tuple to be returned by
1179+
* the scan node for the scanrelid'th RT index, in place of performing an
1180+
* actual table scan. Callers should use EvalPlanQualSlot() to fetch
1181+
* these slots.
11811182
*/
11821183
List*tuple_table;/* tuple table for relsubs_slot */
11831184
TupleTableSlot**relsubs_slot;
@@ -1211,11 +1212,21 @@ typedef struct EPQState
12111212
ExecAuxRowMark**relsubs_rowmark;
12121213

12131214
/*
1214-
* True if a relation's EPQ tuple has been fetched for relation, indexed
1215-
* by scanrelid - 1.
1215+
* relsubs_done[scanrelid - 1] is true if there is no EPQ tuple for this
1216+
* target relation or it has already been fetched in the current scan of
1217+
* this target relation within the current EvalPlanQual test.
12161218
*/
12171219
bool*relsubs_done;
12181220

1221+
/*
1222+
* relsubs_blocked[scanrelid - 1] is true if there is no EPQ tuple for
1223+
* this target relation during the current EvalPlanQual test. We keep
1224+
* these flags set for all relids listed in resultRelations, but
1225+
* transiently clear the one for the relation whose tuple is actually
1226+
* passed to EvalPlanQual().
1227+
*/
1228+
bool*relsubs_blocked;
1229+
12191230
PlanState*recheckplanstate;/* EPQ specific exec nodes, for ->plan */
12201231
}EPQState;
12211232

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp