Skip to content

Commit d487afb

Browse files
committed
Fix PlanRowMark/ExecRowMark structures to handle inheritance correctly.
In an inherited UPDATE/DELETE, each target table has its own subplan, because it might have a column set different from other targets. This means that the resjunk columns we add to support EvalPlanQual might be at different physical column numbers in each subplan. The EvalPlanQual rewrite I did for 9.0 failed to account for this, resulting in possible misbehavior or even crashes during concurrent updates to the same row, as seen in a recent report from Gordon Shannon. Revise the data structure so that we track resjunk column numbers separately for each subplan. I also chose to move responsibility for identifying the physical column numbers back to executor startup, instead of assuming that numbers derived during preprocess_targetlist would stay valid throughout subsequent massaging of the plan. That's a bit slower, so we might want to consider undoing it someday; but it would complicate the patch considerably and didn't seem justifiable in a bug fix that has to be back-patched to 9.0.
1 parent 7a32ff9 commit d487afb

File tree

12 files changed

+192
-122
lines changed

12 files changed

+192
-122
lines changed

src/backend/executor/execJunk.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,22 @@ ExecInitJunkFilterConversion(List *targetList,
207207
*/
208208
AttrNumber
209209
ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
210+
{
211+
return ExecFindJunkAttributeInTlist(junkfilter->jf_targetList, attrName);
212+
}
213+
214+
/*
215+
* ExecFindJunkAttributeInTlist
216+
*
217+
* Find a junk attribute given a subplan's targetlist (not necessarily
218+
* part of a JunkFilter).
219+
*/
220+
AttrNumber
221+
ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
210222
{
211223
ListCell *t;
212224

213-
foreach(t, junkfilter->jf_targetList)
225+
foreach(t, targetlist)
214226
{
215227
TargetEntry *tle = lfirst(t);
216228

src/backend/executor/execMain.c

Lines changed: 82 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -744,9 +744,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
744744
erm->prti = rc->prti;
745745
erm->markType = rc->markType;
746746
erm->noWait = rc->noWait;
747-
erm->ctidAttNo = rc->ctidAttNo;
748-
erm->toidAttNo = rc->toidAttNo;
749-
erm->wholeAttNo = rc->wholeAttNo;
750747
ItemPointerSetInvalid(&(erm->curCtid));
751748
estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
752749
}
@@ -1386,6 +1383,71 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
13861383
}
13871384

13881385

1386+
/*
1387+
* ExecFindRowMark -- find the ExecRowMark struct for given rangetable index
1388+
*/
1389+
ExecRowMark *
1390+
ExecFindRowMark(EState *estate, Index rti)
1391+
{
1392+
ListCell *lc;
1393+
1394+
foreach(lc, estate->es_rowMarks)
1395+
{
1396+
ExecRowMark *erm = (ExecRowMark *) lfirst(lc);
1397+
1398+
if (erm->rti == rti)
1399+
return erm;
1400+
}
1401+
elog(ERROR, "failed to find ExecRowMark for rangetable index %u", rti);
1402+
return NULL; /* keep compiler quiet */
1403+
}
1404+
1405+
/*
1406+
* ExecBuildAuxRowMark -- create an ExecAuxRowMark struct
1407+
*
1408+
* Inputs are the underlying ExecRowMark struct and the targetlist of the
1409+
* input plan node (not planstate node!). We need the latter to find out
1410+
* the column numbers of the resjunk columns.
1411+
*/
1412+
ExecAuxRowMark *
1413+
ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
1414+
{
1415+
ExecAuxRowMark *aerm = (ExecAuxRowMark *) palloc0(sizeof(ExecAuxRowMark));
1416+
char resname[32];
1417+
1418+
aerm->rowmark = erm;
1419+
1420+
/* Look up the resjunk columns associated with this rowmark */
1421+
if (erm->relation)
1422+
{
1423+
Assert(erm->markType != ROW_MARK_COPY);
1424+
1425+
/* if child rel, need tableoid */
1426+
if (erm->rti != erm->prti)
1427+
{
1428+
snprintf(resname, sizeof(resname), "tableoid%u", erm->prti);
1429+
aerm->toidAttNo = ExecFindJunkAttributeInTlist(targetlist,
1430+
resname);
1431+
}
1432+
1433+
/* always need ctid for real relations */
1434+
snprintf(resname, sizeof(resname), "ctid%u", erm->prti);
1435+
aerm->ctidAttNo = ExecFindJunkAttributeInTlist(targetlist,
1436+
resname);
1437+
}
1438+
else
1439+
{
1440+
Assert(erm->markType == ROW_MARK_COPY);
1441+
1442+
snprintf(resname, sizeof(resname), "wholerow%u", erm->prti);
1443+
aerm->wholeAttNo = ExecFindJunkAttributeInTlist(targetlist,
1444+
resname);
1445+
}
1446+
1447+
return aerm;
1448+
}
1449+
1450+
13891451
/*
13901452
* EvalPlanQual logic --- recheck modified tuple(s) to see if we want to
13911453
* process the updated version under READ COMMITTED rules.
@@ -1676,19 +1738,21 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
16761738
/*
16771739
* EvalPlanQualInit -- initialize during creation of a plan state node
16781740
* that might need to invoke EPQ processing.
1679-
* Note: subplan can be NULL if it will be set later with EvalPlanQualSetPlan.
1741+
*
1742+
* Note: subplan/auxrowmarks can be NULL/NIL if they will be set later
1743+
* with EvalPlanQualSetPlan.
16801744
*/
16811745
void
16821746
EvalPlanQualInit(EPQState *epqstate, EState *estate,
1683-
Plan *subplan, int epqParam)
1747+
Plan *subplan, List *auxrowmarks, int epqParam)
16841748
{
16851749
/* Mark the EPQ state inactive */
16861750
epqstate->estate = NULL;
16871751
epqstate->planstate = NULL;
16881752
epqstate->origslot = NULL;
16891753
/* ... and remember data that EvalPlanQualBegin will need */
16901754
epqstate->plan = subplan;
1691-
epqstate->rowMarks = NIL;
1755+
epqstate->arowMarks = auxrowmarks;
16921756
epqstate->epqParam = epqParam;
16931757
}
16941758

@@ -1698,25 +1762,14 @@ EvalPlanQualInit(EPQState *epqstate, EState *estate,
16981762
* We need this so that ModifyTuple can deal with multiple subplans.
16991763
*/
17001764
void
1701-
EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan)
1765+
EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
17021766
{
17031767
/* If we have a live EPQ query, shut it down */
17041768
EvalPlanQualEnd(epqstate);
17051769
/* And set/change the plan pointer */
17061770
epqstate->plan = subplan;
1707-
}
1708-
1709-
/*
1710-
* EvalPlanQualAddRowMark -- add an ExecRowMark that EPQ needs to handle.
1711-
*
1712-
* Currently, only non-locking RowMarks are supported.
1713-
*/
1714-
void
1715-
EvalPlanQualAddRowMark(EPQState *epqstate, ExecRowMark *erm)
1716-
{
1717-
if (RowMarkRequiresRowShareLock(erm->markType))
1718-
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
1719-
epqstate->rowMarks = lappend(epqstate->rowMarks, erm);
1771+
/* The rowmarks depend on the plan, too */
1772+
epqstate->arowMarks = auxrowmarks;
17201773
}
17211774

17221775
/*
@@ -1766,13 +1819,17 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
17661819

17671820
Assert(epqstate->origslot != NULL);
17681821

1769-
foreach(l, epqstate->rowMarks)
1822+
foreach(l, epqstate->arowMarks)
17701823
{
1771-
ExecRowMark *erm = (ExecRowMark *) lfirst(l);
1824+
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(l);
1825+
ExecRowMark *erm = aerm->rowmark;
17721826
Datum datum;
17731827
bool isNull;
17741828
HeapTupleData tuple;
17751829

1830+
if (RowMarkRequiresRowShareLock(erm->markType))
1831+
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
1832+
17761833
/* clear any leftover test tuple for this rel */
17771834
EvalPlanQualSetTuple(epqstate, erm->rti, NULL);
17781835

@@ -1788,7 +1845,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
17881845
Oid tableoid;
17891846

17901847
datum = ExecGetJunkAttribute(epqstate->origslot,
1791-
erm->toidAttNo,
1848+
aerm->toidAttNo,
17921849
&isNull);
17931850
/* non-locked rels could be on the inside of outer joins */
17941851
if (isNull)
@@ -1804,7 +1861,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
18041861

18051862
/* fetch the tuple's ctid */
18061863
datum = ExecGetJunkAttribute(epqstate->origslot,
1807-
erm->ctidAttNo,
1864+
aerm->ctidAttNo,
18081865
&isNull);
18091866
/* non-locked rels could be on the inside of outer joins */
18101867
if (isNull)
@@ -1829,7 +1886,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
18291886

18301887
/* fetch the whole-row Var for the relation */
18311888
datum = ExecGetJunkAttribute(epqstate->origslot,
1832-
erm->wholeAttNo,
1889+
aerm->wholeAttNo,
18331890
&isNull);
18341891
/* non-locked rels could be on the inside of outer joins */
18351892
if (isNull)

src/backend/executor/nodeLockRows.c

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,13 @@ ExecLockRows(LockRowsState *node)
5858

5959
/*
6060
* Attempt to lock the source tuple(s). (Note we only have locking
61-
* rowmarks in lr_rowMarks.)
61+
* rowmarks in lr_arowMarks.)
6262
*/
6363
epq_started = false;
64-
foreach(lc, node->lr_rowMarks)
64+
foreach(lc, node->lr_arowMarks)
6565
{
66-
ExecRowMark *erm = (ExecRowMark *) lfirst(lc);
66+
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
67+
ExecRowMark *erm = aerm->rowmark;
6768
Datum datum;
6869
bool isNull;
6970
HeapTupleData tuple;
@@ -84,7 +85,7 @@ ExecLockRows(LockRowsState *node)
8485
Oid tableoid;
8586

8687
datum = ExecGetJunkAttribute(slot,
87-
erm->toidAttNo,
88+
aerm->toidAttNo,
8889
&isNull);
8990
/* shouldn't ever get a null result... */
9091
if (isNull)
@@ -101,7 +102,7 @@ ExecLockRows(LockRowsState *node)
101102

102103
/* fetch the tuple's ctid */
103104
datum = ExecGetJunkAttribute(slot,
104-
erm->ctidAttNo,
105+
aerm->ctidAttNo,
105106
&isNull);
106107
/* shouldn't ever get a null result... */
107108
if (isNull)
@@ -189,9 +190,10 @@ ExecLockRows(LockRowsState *node)
189190
* so as to avoid overhead in the common case where there are no
190191
* concurrent updates.)
191192
*/
192-
foreach(lc, node->lr_rowMarks)
193+
foreach(lc, node->lr_arowMarks)
193194
{
194-
ExecRowMark *erm = (ExecRowMark *) lfirst(lc);
195+
ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
196+
ExecRowMark *erm = aerm->rowmark;
195197
HeapTupleData tuple;
196198
Buffer buffer;
197199

@@ -251,6 +253,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
251253
{
252254
LockRowsState *lrstate;
253255
Plan *outerPlan = outerPlan(node);
256+
List *epq_arowmarks;
254257
ListCell *lc;
255258

256259
/* check for unsupported flags */
@@ -262,7 +265,6 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
262265
lrstate = makeNode(LockRowsState);
263266
lrstate->ps.plan = (Plan *) node;
264267
lrstate->ps.state = estate;
265-
EvalPlanQualInit(&lrstate->lr_epqstate, estate, outerPlan, node->epqParam);
266268

267269
/*
268270
* Miscellaneous initialization
@@ -288,32 +290,27 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
288290
lrstate->ps.ps_ProjInfo = NULL;
289291

290292
/*
291-
* Locate the ExecRowMark(s) that this node is responsible for. (InitPlan
292-
* should already have built the global list of ExecRowMarks.)
293+
* Locate the ExecRowMark(s) that this node is responsible for, and
294+
* construct ExecAuxRowMarks for them. (InitPlan should already have
295+
* built the global list of ExecRowMarks.)
293296
*/
294-
lrstate->lr_rowMarks = NIL;
297+
lrstate->lr_arowMarks = NIL;
298+
epq_arowmarks = NIL;
295299
foreach(lc, node->rowMarks)
296300
{
297301
PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
298-
ExecRowMark *erm = NULL;
299-
ListCell *lce;
302+
ExecRowMark *erm;
303+
ExecAuxRowMark *aerm;
300304

301305
Assert(IsA(rc, PlanRowMark));
302306

303307
/* ignore "parent" rowmarks; they are irrelevant at runtime */
304308
if (rc->isParent)
305309
continue;
306310

307-
foreach(lce, estate->es_rowMarks)
308-
{
309-
erm = (ExecRowMark *) lfirst(lce);
310-
if (erm->rti == rc->rti)
311-
break;
312-
erm = NULL;
313-
}
314-
if (erm == NULL)
315-
elog(ERROR, "failed to find ExecRowMark for PlanRowMark %u",
316-
rc->rti);
311+
/* find ExecRowMark and build ExecAuxRowMark */
312+
erm = ExecFindRowMark(estate, rc->rti);
313+
aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
317314

318315
/*
319316
* Only locking rowmarks go into our own list. Non-locking marks are
@@ -322,11 +319,15 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
322319
* do an EPQ recheck.
323320
*/
324321
if (RowMarkRequiresRowShareLock(erm->markType))
325-
lrstate->lr_rowMarks = lappend(lrstate->lr_rowMarks, erm);
322+
lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
326323
else
327-
EvalPlanQualAddRowMark(&lrstate->lr_epqstate, erm);
324+
epq_arowmarks = lappend(epq_arowmarks, aerm);
328325
}
329326

327+
/* Now we have the info needed to set up EPQ state */
328+
EvalPlanQualInit(&lrstate->lr_epqstate, estate,
329+
outerPlan, epq_arowmarks, node->epqParam);
330+
330331
return lrstate;
331332
}
332333

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy