Commit d487afbb authored by Tom Lane's avatar Tom Lane

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.
parent 7a32ff97
...@@ -207,10 +207,22 @@ ExecInitJunkFilterConversion(List *targetList, ...@@ -207,10 +207,22 @@ ExecInitJunkFilterConversion(List *targetList,
*/ */
AttrNumber AttrNumber
ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName) ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
{
return ExecFindJunkAttributeInTlist(junkfilter->jf_targetList, attrName);
}
/*
* ExecFindJunkAttributeInTlist
*
* Find a junk attribute given a subplan's targetlist (not necessarily
* part of a JunkFilter).
*/
AttrNumber
ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
{ {
ListCell *t; ListCell *t;
foreach(t, junkfilter->jf_targetList) foreach(t, targetlist)
{ {
TargetEntry *tle = lfirst(t); TargetEntry *tle = lfirst(t);
......
...@@ -744,9 +744,6 @@ InitPlan(QueryDesc *queryDesc, int eflags) ...@@ -744,9 +744,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
erm->prti = rc->prti; erm->prti = rc->prti;
erm->markType = rc->markType; erm->markType = rc->markType;
erm->noWait = rc->noWait; erm->noWait = rc->noWait;
erm->ctidAttNo = rc->ctidAttNo;
erm->toidAttNo = rc->toidAttNo;
erm->wholeAttNo = rc->wholeAttNo;
ItemPointerSetInvalid(&(erm->curCtid)); ItemPointerSetInvalid(&(erm->curCtid));
estate->es_rowMarks = lappend(estate->es_rowMarks, erm); estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
} }
...@@ -1386,6 +1383,71 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -1386,6 +1383,71 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
} }
/*
* ExecFindRowMark -- find the ExecRowMark struct for given rangetable index
*/
ExecRowMark *
ExecFindRowMark(EState *estate, Index rti)
{
ListCell *lc;
foreach(lc, estate->es_rowMarks)
{
ExecRowMark *erm = (ExecRowMark *) lfirst(lc);
if (erm->rti == rti)
return erm;
}
elog(ERROR, "failed to find ExecRowMark for rangetable index %u", rti);
return NULL; /* keep compiler quiet */
}
/*
* ExecBuildAuxRowMark -- create an ExecAuxRowMark struct
*
* Inputs are the underlying ExecRowMark struct and the targetlist of the
* input plan node (not planstate node!). We need the latter to find out
* the column numbers of the resjunk columns.
*/
ExecAuxRowMark *
ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
{
ExecAuxRowMark *aerm = (ExecAuxRowMark *) palloc0(sizeof(ExecAuxRowMark));
char resname[32];
aerm->rowmark = erm;
/* Look up the resjunk columns associated with this rowmark */
if (erm->relation)
{
Assert(erm->markType != ROW_MARK_COPY);
/* if child rel, need tableoid */
if (erm->rti != erm->prti)
{
snprintf(resname, sizeof(resname), "tableoid%u", erm->prti);
aerm->toidAttNo = ExecFindJunkAttributeInTlist(targetlist,
resname);
}
/* always need ctid for real relations */
snprintf(resname, sizeof(resname), "ctid%u", erm->prti);
aerm->ctidAttNo = ExecFindJunkAttributeInTlist(targetlist,
resname);
}
else
{
Assert(erm->markType == ROW_MARK_COPY);
snprintf(resname, sizeof(resname), "wholerow%u", erm->prti);
aerm->wholeAttNo = ExecFindJunkAttributeInTlist(targetlist,
resname);
}
return aerm;
}
/* /*
* EvalPlanQual logic --- recheck modified tuple(s) to see if we want to * EvalPlanQual logic --- recheck modified tuple(s) to see if we want to
* process the updated version under READ COMMITTED rules. * process the updated version under READ COMMITTED rules.
...@@ -1676,11 +1738,13 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, ...@@ -1676,11 +1738,13 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/* /*
* EvalPlanQualInit -- initialize during creation of a plan state node * EvalPlanQualInit -- initialize during creation of a plan state node
* that might need to invoke EPQ processing. * that might need to invoke EPQ processing.
* Note: subplan can be NULL if it will be set later with EvalPlanQualSetPlan. *
* Note: subplan/auxrowmarks can be NULL/NIL if they will be set later
* with EvalPlanQualSetPlan.
*/ */
void void
EvalPlanQualInit(EPQState *epqstate, EState *estate, EvalPlanQualInit(EPQState *epqstate, EState *estate,
Plan *subplan, int epqParam) Plan *subplan, List *auxrowmarks, int epqParam)
{ {
/* Mark the EPQ state inactive */ /* Mark the EPQ state inactive */
epqstate->estate = NULL; epqstate->estate = NULL;
...@@ -1688,7 +1752,7 @@ EvalPlanQualInit(EPQState *epqstate, EState *estate, ...@@ -1688,7 +1752,7 @@ EvalPlanQualInit(EPQState *epqstate, EState *estate,
epqstate->origslot = NULL; epqstate->origslot = NULL;
/* ... and remember data that EvalPlanQualBegin will need */ /* ... and remember data that EvalPlanQualBegin will need */
epqstate->plan = subplan; epqstate->plan = subplan;
epqstate->rowMarks = NIL; epqstate->arowMarks = auxrowmarks;
epqstate->epqParam = epqParam; epqstate->epqParam = epqParam;
} }
...@@ -1698,25 +1762,14 @@ EvalPlanQualInit(EPQState *epqstate, EState *estate, ...@@ -1698,25 +1762,14 @@ EvalPlanQualInit(EPQState *epqstate, EState *estate,
* We need this so that ModifyTuple can deal with multiple subplans. * We need this so that ModifyTuple can deal with multiple subplans.
*/ */
void void
EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan) EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
{ {
/* If we have a live EPQ query, shut it down */ /* If we have a live EPQ query, shut it down */
EvalPlanQualEnd(epqstate); EvalPlanQualEnd(epqstate);
/* And set/change the plan pointer */ /* And set/change the plan pointer */
epqstate->plan = subplan; epqstate->plan = subplan;
} /* The rowmarks depend on the plan, too */
epqstate->arowMarks = auxrowmarks;
/*
* EvalPlanQualAddRowMark -- add an ExecRowMark that EPQ needs to handle.
*
* Currently, only non-locking RowMarks are supported.
*/
void
EvalPlanQualAddRowMark(EPQState *epqstate, ExecRowMark *erm)
{
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
epqstate->rowMarks = lappend(epqstate->rowMarks, erm);
} }
/* /*
...@@ -1766,13 +1819,17 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) ...@@ -1766,13 +1819,17 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
Assert(epqstate->origslot != NULL); Assert(epqstate->origslot != NULL);
foreach(l, epqstate->rowMarks) foreach(l, epqstate->arowMarks)
{ {
ExecRowMark *erm = (ExecRowMark *) lfirst(l); ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(l);
ExecRowMark *erm = aerm->rowmark;
Datum datum; Datum datum;
bool isNull; bool isNull;
HeapTupleData tuple; HeapTupleData tuple;
if (RowMarkRequiresRowShareLock(erm->markType))
elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
/* clear any leftover test tuple for this rel */ /* clear any leftover test tuple for this rel */
EvalPlanQualSetTuple(epqstate, erm->rti, NULL); EvalPlanQualSetTuple(epqstate, erm->rti, NULL);
...@@ -1788,7 +1845,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) ...@@ -1788,7 +1845,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
Oid tableoid; Oid tableoid;
datum = ExecGetJunkAttribute(epqstate->origslot, datum = ExecGetJunkAttribute(epqstate->origslot,
erm->toidAttNo, aerm->toidAttNo,
&isNull); &isNull);
/* non-locked rels could be on the inside of outer joins */ /* non-locked rels could be on the inside of outer joins */
if (isNull) if (isNull)
...@@ -1804,7 +1861,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) ...@@ -1804,7 +1861,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* fetch the tuple's ctid */ /* fetch the tuple's ctid */
datum = ExecGetJunkAttribute(epqstate->origslot, datum = ExecGetJunkAttribute(epqstate->origslot,
erm->ctidAttNo, aerm->ctidAttNo,
&isNull); &isNull);
/* non-locked rels could be on the inside of outer joins */ /* non-locked rels could be on the inside of outer joins */
if (isNull) if (isNull)
...@@ -1829,7 +1886,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) ...@@ -1829,7 +1886,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* fetch the whole-row Var for the relation */ /* fetch the whole-row Var for the relation */
datum = ExecGetJunkAttribute(epqstate->origslot, datum = ExecGetJunkAttribute(epqstate->origslot,
erm->wholeAttNo, aerm->wholeAttNo,
&isNull); &isNull);
/* non-locked rels could be on the inside of outer joins */ /* non-locked rels could be on the inside of outer joins */
if (isNull) if (isNull)
......
...@@ -58,12 +58,13 @@ lnext: ...@@ -58,12 +58,13 @@ lnext:
/* /*
* Attempt to lock the source tuple(s). (Note we only have locking * Attempt to lock the source tuple(s). (Note we only have locking
* rowmarks in lr_rowMarks.) * rowmarks in lr_arowMarks.)
*/ */
epq_started = false; epq_started = false;
foreach(lc, node->lr_rowMarks) foreach(lc, node->lr_arowMarks)
{ {
ExecRowMark *erm = (ExecRowMark *) lfirst(lc); ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
Datum datum; Datum datum;
bool isNull; bool isNull;
HeapTupleData tuple; HeapTupleData tuple;
...@@ -84,7 +85,7 @@ lnext: ...@@ -84,7 +85,7 @@ lnext:
Oid tableoid; Oid tableoid;
datum = ExecGetJunkAttribute(slot, datum = ExecGetJunkAttribute(slot,
erm->toidAttNo, aerm->toidAttNo,
&isNull); &isNull);
/* shouldn't ever get a null result... */ /* shouldn't ever get a null result... */
if (isNull) if (isNull)
...@@ -101,7 +102,7 @@ lnext: ...@@ -101,7 +102,7 @@ lnext:
/* fetch the tuple's ctid */ /* fetch the tuple's ctid */
datum = ExecGetJunkAttribute(slot, datum = ExecGetJunkAttribute(slot,
erm->ctidAttNo, aerm->ctidAttNo,
&isNull); &isNull);
/* shouldn't ever get a null result... */ /* shouldn't ever get a null result... */
if (isNull) if (isNull)
...@@ -189,9 +190,10 @@ lnext: ...@@ -189,9 +190,10 @@ lnext:
* so as to avoid overhead in the common case where there are no * so as to avoid overhead in the common case where there are no
* concurrent updates.) * concurrent updates.)
*/ */
foreach(lc, node->lr_rowMarks) foreach(lc, node->lr_arowMarks)
{ {
ExecRowMark *erm = (ExecRowMark *) lfirst(lc); ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
ExecRowMark *erm = aerm->rowmark;
HeapTupleData tuple; HeapTupleData tuple;
Buffer buffer; Buffer buffer;
...@@ -251,6 +253,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) ...@@ -251,6 +253,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
{ {
LockRowsState *lrstate; LockRowsState *lrstate;
Plan *outerPlan = outerPlan(node); Plan *outerPlan = outerPlan(node);
List *epq_arowmarks;
ListCell *lc; ListCell *lc;
/* check for unsupported flags */ /* check for unsupported flags */
...@@ -262,7 +265,6 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) ...@@ -262,7 +265,6 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
lrstate = makeNode(LockRowsState); lrstate = makeNode(LockRowsState);
lrstate->ps.plan = (Plan *) node; lrstate->ps.plan = (Plan *) node;
lrstate->ps.state = estate; lrstate->ps.state = estate;
EvalPlanQualInit(&lrstate->lr_epqstate, estate, outerPlan, node->epqParam);
/* /*
* Miscellaneous initialization * Miscellaneous initialization
...@@ -288,15 +290,17 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) ...@@ -288,15 +290,17 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
lrstate->ps.ps_ProjInfo = NULL; lrstate->ps.ps_ProjInfo = NULL;
/* /*
* Locate the ExecRowMark(s) that this node is responsible for. (InitPlan * Locate the ExecRowMark(s) that this node is responsible for, and
* should already have built the global list of ExecRowMarks.) * construct ExecAuxRowMarks for them. (InitPlan should already have
* built the global list of ExecRowMarks.)
*/ */
lrstate->lr_rowMarks = NIL; lrstate->lr_arowMarks = NIL;
epq_arowmarks = NIL;
foreach(lc, node->rowMarks) foreach(lc, node->rowMarks)
{ {
PlanRowMark *rc = (PlanRowMark *) lfirst(lc); PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
ExecRowMark *erm = NULL; ExecRowMark *erm;
ListCell *lce; ExecAuxRowMark *aerm;
Assert(IsA(rc, PlanRowMark)); Assert(IsA(rc, PlanRowMark));
...@@ -304,16 +308,9 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) ...@@ -304,16 +308,9 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
if (rc->isParent) if (rc->isParent)
continue; continue;
foreach(lce, estate->es_rowMarks) /* find ExecRowMark and build ExecAuxRowMark */
{ erm = ExecFindRowMark(estate, rc->rti);
erm = (ExecRowMark *) lfirst(lce); aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
if (erm->rti == rc->rti)
break;
erm = NULL;
}
if (erm == NULL)
elog(ERROR, "failed to find ExecRowMark for PlanRowMark %u",
rc->rti);
/* /*
* Only locking rowmarks go into our own list. Non-locking marks are * Only locking rowmarks go into our own list. Non-locking marks are
...@@ -322,11 +319,15 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) ...@@ -322,11 +319,15 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* do an EPQ recheck. * do an EPQ recheck.
*/ */
if (RowMarkRequiresRowShareLock(erm->markType)) if (RowMarkRequiresRowShareLock(erm->markType))
lrstate->lr_rowMarks = lappend(lrstate->lr_rowMarks, erm); lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
else else
EvalPlanQualAddRowMark(&lrstate->lr_epqstate, erm); epq_arowmarks = lappend(epq_arowmarks, aerm);
} }
/* Now we have the info needed to set up EPQ state */
EvalPlanQualInit(&lrstate->lr_epqstate, estate,
outerPlan, epq_arowmarks, node->epqParam);
return lrstate; return lrstate;
} }
......
...@@ -824,7 +824,8 @@ ExecModifyTable(ModifyTableState *node) ...@@ -824,7 +824,8 @@ ExecModifyTable(ModifyTableState *node)
estate->es_result_relation_info++; estate->es_result_relation_info++;
subplanstate = node->mt_plans[node->mt_whichplan]; subplanstate = node->mt_plans[node->mt_whichplan];
junkfilter = estate->es_result_relation_info->ri_junkFilter; junkfilter = estate->es_result_relation_info->ri_junkFilter;
EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan); EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,
node->mt_arowmarks[node->mt_whichplan]);
continue; continue;
} }
else else
...@@ -953,10 +954,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ...@@ -953,10 +954,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->ps.targetlist = NIL; /* not actually used */ mtstate->ps.targetlist = NIL; /* not actually used */
mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans); mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
mtstate->mt_nplans = nplans; mtstate->mt_nplans = nplans;
mtstate->operation = operation; mtstate->operation = operation;
/* set up epqstate with dummy subplan pointer for the moment */ /* set up epqstate with dummy subplan data for the moment */
EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, node->epqParam); EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
mtstate->fireBSTriggers = true; mtstate->fireBSTriggers = true;
/* For the moment, assume our targets are exactly the global result rels */ /* For the moment, assume our targets are exactly the global result rels */
...@@ -978,11 +980,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ...@@ -978,11 +980,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
} }
estate->es_result_relation_info = NULL; estate->es_result_relation_info = NULL;
/* select first subplan */
mtstate->mt_whichplan = 0;
subplan = (Plan *) linitial(node->plans);
EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan);
/* /*
* Initialize RETURNING projections if needed. * Initialize RETURNING projections if needed.
*/ */
...@@ -1046,8 +1043,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ...@@ -1046,8 +1043,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
foreach(l, node->rowMarks) foreach(l, node->rowMarks)
{ {
PlanRowMark *rc = (PlanRowMark *) lfirst(l); PlanRowMark *rc = (PlanRowMark *) lfirst(l);
ExecRowMark *erm = NULL; ExecRowMark *erm;
ListCell *lce;
Assert(IsA(rc, PlanRowMark)); Assert(IsA(rc, PlanRowMark));
...@@ -1055,19 +1051,25 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) ...@@ -1055,19 +1051,25 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (rc->isParent) if (rc->isParent)
continue; continue;
foreach(lce, estate->es_rowMarks) /* find ExecRowMark (same for all subplans) */
erm = ExecFindRowMark(estate, rc->rti);
/* build ExecAuxRowMark for each subplan */
for (i = 0; i < nplans; i++)
{ {
erm = (ExecRowMark *) lfirst(lce); ExecAuxRowMark *aerm;
if (erm->rti == rc->rti)
break;
erm = NULL;
}
if (erm == NULL)
elog(ERROR, "failed to find ExecRowMark for PlanRowMark %u",
rc->rti);
EvalPlanQualAddRowMark(&mtstate->mt_epqstate, erm); subplan = mtstate->mt_plans[i]->plan;
aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
mtstate->mt_arowmarks[i] = lappend(mtstate->mt_arowmarks[i], aerm);
} }
}
/* select first subplan */
mtstate->mt_whichplan = 0;
subplan = (Plan *) linitial(node->plans);
EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan,
mtstate->mt_arowmarks[0]);
/* /*
* Initialize the junk filter(s) if needed. INSERT queries need a filter * Initialize the junk filter(s) if needed. INSERT queries need a filter
......
...@@ -905,9 +905,6 @@ _copyPlanRowMark(PlanRowMark *from) ...@@ -905,9 +905,6 @@ _copyPlanRowMark(PlanRowMark *from)
COPY_SCALAR_FIELD(markType); COPY_SCALAR_FIELD(markType);
COPY_SCALAR_FIELD(noWait); COPY_SCALAR_FIELD(noWait);
COPY_SCALAR_FIELD(isParent); COPY_SCALAR_FIELD(isParent);
COPY_SCALAR_FIELD(ctidAttNo);
COPY_SCALAR_FIELD(toidAttNo);
COPY_SCALAR_FIELD(wholeAttNo);
return newnode; return newnode;
} }
......
...@@ -798,9 +798,6 @@ _outPlanRowMark(StringInfo str, PlanRowMark *node) ...@@ -798,9 +798,6 @@ _outPlanRowMark(StringInfo str, PlanRowMark *node)
WRITE_ENUM_FIELD(markType, RowMarkType); WRITE_ENUM_FIELD(markType, RowMarkType);
WRITE_BOOL_FIELD(noWait); WRITE_BOOL_FIELD(noWait);
WRITE_BOOL_FIELD(isParent); WRITE_BOOL_FIELD(isParent);
WRITE_INT_FIELD(ctidAttNo);
WRITE_INT_FIELD(toidAttNo);
WRITE_INT_FIELD(wholeAttNo);
} }
static void static void
......
...@@ -1929,10 +1929,6 @@ preprocess_rowmarks(PlannerInfo *root) ...@@ -1929,10 +1929,6 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->markType = ROW_MARK_SHARE; newrc->markType = ROW_MARK_SHARE;
newrc->noWait = rc->noWait; newrc->noWait = rc->noWait;
newrc->isParent = false; newrc->isParent = false;
/* attnos will be assigned in preprocess_targetlist */
newrc->ctidAttNo = InvalidAttrNumber;
newrc->toidAttNo = InvalidAttrNumber;
newrc->wholeAttNo = InvalidAttrNumber;
prowmarks = lappend(prowmarks, newrc); prowmarks = lappend(prowmarks, newrc);
} }
...@@ -1959,10 +1955,6 @@ preprocess_rowmarks(PlannerInfo *root) ...@@ -1959,10 +1955,6 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->markType = ROW_MARK_COPY; newrc->markType = ROW_MARK_COPY;
newrc->noWait = false; /* doesn't matter */ newrc->noWait = false; /* doesn't matter */
newrc->isParent = false; newrc->isParent = false;
/* attnos will be assigned in preprocess_targetlist */
newrc->ctidAttNo = InvalidAttrNumber;
newrc->toidAttNo = InvalidAttrNumber;
newrc->wholeAttNo = InvalidAttrNumber;
prowmarks = lappend(prowmarks, newrc); prowmarks = lappend(prowmarks, newrc);
} }
......
...@@ -80,8 +80,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) ...@@ -80,8 +80,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
/* /*
* Add necessary junk columns for rowmarked rels. These values are needed * Add necessary junk columns for rowmarked rels. These values are needed
* for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
* rechecking. While we are at it, store these junk attnos in the * rechecking. See comments for PlanRowMark in plannodes.h.
* PlanRowMark list so that we don't have to redetermine them at runtime.
*/ */
foreach(lc, root->rowMarks) foreach(lc, root->rowMarks)
{ {
...@@ -90,18 +89,9 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) ...@@ -90,18 +89,9 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
char resname[32]; char resname[32];
TargetEntry *tle; TargetEntry *tle;
/* child rels should just use the same junk attrs as their parents */ /* child rels use the same junk attrs as their parents */
if (rc->rti != rc->prti) if (rc->rti != rc->prti)
{
PlanRowMark *prc = get_plan_rowmark(root->rowMarks, rc->prti);
/* parent should have appeared earlier in list */
if (prc == NULL || prc->toidAttNo == InvalidAttrNumber)
elog(ERROR, "parent PlanRowMark not processed yet");
rc->ctidAttNo = prc->ctidAttNo;
rc->toidAttNo = prc->toidAttNo;
continue; continue;
}
if (rc->markType != ROW_MARK_COPY) if (rc->markType != ROW_MARK_COPY)
{ {
...@@ -117,7 +107,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) ...@@ -117,7 +107,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
pstrdup(resname), pstrdup(resname),
true); true);
tlist = lappend(tlist, tle); tlist = lappend(tlist, tle);
rc->ctidAttNo = tle->resno;
/* if parent of inheritance tree, need the tableoid too */ /* if parent of inheritance tree, need the tableoid too */
if (rc->isParent) if (rc->isParent)
...@@ -133,7 +122,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) ...@@ -133,7 +122,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
pstrdup(resname), pstrdup(resname),
true); true);
tlist = lappend(tlist, tle); tlist = lappend(tlist, tle);
rc->toidAttNo = tle->resno;
} }
} }
else else
...@@ -148,7 +136,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) ...@@ -148,7 +136,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
pstrdup(resname), pstrdup(resname),
true); true);
tlist = lappend(tlist, tle); tlist = lappend(tlist, tle);
rc->wholeAttNo = tle->resno;
} }
} }
......
...@@ -1292,10 +1292,6 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) ...@@ -1292,10 +1292,6 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
newrc->markType = oldrc->markType; newrc->markType = oldrc->markType;
newrc->noWait = oldrc->noWait; newrc->noWait = oldrc->noWait;
newrc->isParent = false; newrc->isParent = false;
/* junk attrs for children are not identified yet */
newrc->ctidAttNo = InvalidAttrNumber;
newrc->toidAttNo = InvalidAttrNumber;
newrc->wholeAttNo = InvalidAttrNumber;
root->rowMarks = lappend(root->rowMarks, newrc); root->rowMarks = lappend(root->rowMarks, newrc);
} }
......
...@@ -142,6 +142,8 @@ extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, ...@@ -142,6 +142,8 @@ extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
TupleTableSlot *slot); TupleTableSlot *slot);
extern AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter, extern AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter,
const char *attrName); const char *attrName);
extern AttrNumber ExecFindJunkAttributeInTlist(List *targetlist,
const char *attrName);
extern Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, extern Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno,
bool *isNull); bool *isNull);
extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter, extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter,
...@@ -171,15 +173,17 @@ extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); ...@@ -171,15 +173,17 @@ extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
extern void ExecConstraints(ResultRelInfo *resultRelInfo, extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate); TupleTableSlot *slot, EState *estate);
extern ExecRowMark *ExecFindRowMark(EState *estate, Index rti);
extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
Relation relation, Index rti, Relation relation, Index rti,
ItemPointer tid, TransactionId priorXmax); ItemPointer tid, TransactionId priorXmax);
extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation, extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
int lockmode, ItemPointer tid, TransactionId priorXmax); int lockmode, ItemPointer tid, TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate, extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
Plan *subplan, int epqParam); Plan *subplan, List *auxrowmarks, int epqParam);
extern void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan); extern void EvalPlanQualSetPlan(EPQState *epqstate,
extern void EvalPlanQualAddRowMark(EPQState *epqstate, ExecRowMark *erm); Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti, extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
HeapTuple tuple); HeapTuple tuple);
extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti); extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
......
...@@ -407,12 +407,10 @@ typedef struct EState ...@@ -407,12 +407,10 @@ typedef struct EState
* When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE, we should have an * When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE, we should have an
* ExecRowMark for each non-target relation in the query (except inheritance * ExecRowMark for each non-target relation in the query (except inheritance
* parent RTEs, which can be ignored at runtime). See PlanRowMark for details * parent RTEs, which can be ignored at runtime). See PlanRowMark for details
* about most of the fields. * about most of the fields. In addition to fields directly derived from
* PlanRowMark, we store curCtid, which is used by the WHERE CURRENT OF code.
* *
* es_rowMarks is a list of these structs. Each LockRows node has its own * EState->es_rowMarks is a list of these structs.
* list, which is the subset of locks that it is supposed to enforce; note
* that the per-node lists point to the same structs that are in the global
* list.
*/ */
typedef struct ExecRowMark typedef struct ExecRowMark
{ {
...@@ -421,11 +419,28 @@ typedef struct ExecRowMark ...@@ -421,11 +419,28 @@ typedef struct ExecRowMark
Index prti; /* parent range table index, if child */ Index prti; /* parent range table index, if child */
RowMarkType markType; /* see enum in nodes/plannodes.h */ RowMarkType markType; /* see enum in nodes/plannodes.h */
bool noWait; /* NOWAIT option */ bool noWait; /* NOWAIT option */
ItemPointerData curCtid; /* ctid of currently locked tuple, if any */
} ExecRowMark;
/*
* ExecAuxRowMark -
* additional runtime representation of FOR UPDATE/SHARE clauses
*
* Each LockRows and ModifyTable node keeps a list of the rowmarks it needs to
* deal with. In addition to a pointer to the related entry in es_rowMarks,
* this struct carries the column number(s) of the resjunk columns associated
* with the rowmark (see comments for PlanRowMark for more detail). In the
* case of ModifyTable, there has to be a separate ExecAuxRowMark list for
* each child plan, because the resjunk columns could be at different physical
* column positions in different subplans.
*/
typedef struct ExecAuxRowMark
{
ExecRowMark *rowmark; /* related entry in es_rowMarks */
AttrNumber ctidAttNo; /* resno of ctid junk attribute, if any */ AttrNumber ctidAttNo; /* resno of ctid junk attribute, if any */
AttrNumber toidAttNo; /* resno of tableoid junk attribute, if any */ AttrNumber toidAttNo; /* resno of tableoid junk attribute, if any */
AttrNumber wholeAttNo; /* resno of whole-row junk attribute, if any */ AttrNumber wholeAttNo; /* resno of whole-row junk attribute, if any */
ItemPointerData curCtid; /* ctid of currently locked tuple, if any */ } ExecAuxRowMark;
} ExecRowMark;
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
...@@ -1001,7 +1016,7 @@ typedef struct EPQState ...@@ -1001,7 +1016,7 @@ typedef struct EPQState
PlanState *planstate; /* plan state tree ready to be executed */ PlanState *planstate; /* plan state tree ready to be executed */
TupleTableSlot *origslot; /* original output tuple to be rechecked */ TupleTableSlot *origslot; /* original output tuple to be rechecked */
Plan *plan; /* plan tree to be executed */ Plan *plan; /* plan tree to be executed */
List *rowMarks; /* ExecRowMarks (non-locking only) */ List *arowMarks; /* ExecAuxRowMarks (non-locking only) */
int epqParam; /* ID of Param to force scan node re-eval */ int epqParam; /* ID of Param to force scan node re-eval */
} EPQState; } EPQState;
...@@ -1029,6 +1044,7 @@ typedef struct ModifyTableState ...@@ -1029,6 +1044,7 @@ typedef struct ModifyTableState
PlanState **mt_plans; /* subplans (one per target rel) */ PlanState **mt_plans; /* subplans (one per target rel) */
int mt_nplans; /* number of plans in the array */ int mt_nplans; /* number of plans in the array */
int mt_whichplan; /* which one is being executed (0..n-1) */ int mt_whichplan; /* which one is being executed (0..n-1) */
List **mt_arowmarks; /* per-subplan ExecAuxRowMark lists */
EPQState mt_epqstate; /* for evaluating EvalPlanQual rechecks */ EPQState mt_epqstate; /* for evaluating EvalPlanQual rechecks */
bool fireBSTriggers; /* do we need to fire stmt triggers? */ bool fireBSTriggers; /* do we need to fire stmt triggers? */
} ModifyTableState; } ModifyTableState;
...@@ -1739,7 +1755,7 @@ typedef struct SetOpState ...@@ -1739,7 +1755,7 @@ typedef struct SetOpState
typedef struct LockRowsState typedef struct LockRowsState
{ {
PlanState ps; /* its first field is NodeTag */ PlanState ps; /* its first field is NodeTag */
List *lr_rowMarks; /* List of ExecRowMarks */ List *lr_arowMarks; /* List of ExecAuxRowMarks */
EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */ EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */
} LockRowsState; } LockRowsState;
......
...@@ -158,6 +158,9 @@ typedef struct Result ...@@ -158,6 +158,9 @@ typedef struct Result
* ModifyTable node - * ModifyTable node -
* Apply rows produced by subplan(s) to result table(s), * Apply rows produced by subplan(s) to result table(s),
* by inserting, updating, or deleting. * by inserting, updating, or deleting.
*
* Note that rowMarks and epqParam are presumed to be valid for all the
* subplan(s); they can't contain any info that varies across subplans.
* ---------------- * ----------------
*/ */
typedef struct ModifyTable typedef struct ModifyTable
...@@ -734,9 +737,18 @@ typedef enum RowMarkType ...@@ -734,9 +737,18 @@ typedef enum RowMarkType
* prti == parent's RT index, and can therefore be recognized as children by * prti == parent's RT index, and can therefore be recognized as children by
* the fact that prti != rti. * the fact that prti != rti.
* *
* The AttrNumbers are filled in during preprocess_targetlist. We use * The planner also adds resjunk output columns to the plan that carry
* different subsets of them for plain relations, inheritance children, * information sufficient to identify the locked or fetched rows. For
* and non-table relations. * tables (markType != ROW_MARK_COPY), these columns are named
* tableoid%u OID of table
* ctid%u TID of row
* The tableoid column is only present for an inheritance hierarchy.
* When markType == ROW_MARK_COPY, there is instead a single column named
* wholerow%u whole-row value of relation
* In all three cases, %u represents the parent rangetable index (prti).
* Note this means that all tables in an inheritance hierarchy share the
* same resjunk column names. However, in an inherited UPDATE/DELETE the
* columns could have different physical column numbers in each subplan.
*/ */
typedef struct PlanRowMark typedef struct PlanRowMark
{ {
...@@ -746,9 +758,6 @@ typedef struct PlanRowMark ...@@ -746,9 +758,6 @@ typedef struct PlanRowMark
RowMarkType markType; /* see enum above */ RowMarkType markType; /* see enum above */
bool noWait; /* NOWAIT option */ bool noWait; /* NOWAIT option */
bool isParent; /* true if this is a "dummy" parent entry */ bool isParent; /* true if this is a "dummy" parent entry */
AttrNumber ctidAttNo; /* resno of ctid junk attribute, if any */
AttrNumber toidAttNo; /* resno of tableoid junk attribute, if any */
AttrNumber wholeAttNo; /* resno of whole-row junk attribute, if any */
} PlanRowMark; } PlanRowMark;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment