Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+13-1
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

+82-25
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

+26-25
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)