mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
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.
This commit is contained in:
@ -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)
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
|||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
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,
|
|||||||
* 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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
/* 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)
|
|||||||
|
|
||||||
/* 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:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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:
|
|||||||
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:
|
|||||||
|
|
||||||
/* 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:
|
|||||||
* 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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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)
|
|||||||
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,20 +1051,26 @@ 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);
|
||||||
erm = (ExecRowMark *) lfirst(lce);
|
|
||||||
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);
|
/* build ExecAuxRowMark for each subplan */
|
||||||
|
for (i = 0; i < nplans; i++)
|
||||||
|
{
|
||||||
|
ExecAuxRowMark *aerm;
|
||||||
|
|
||||||
|
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
|
||||||
* if there are any junk attrs in the tlist. UPDATE and DELETE always
|
* if there are any junk attrs in the tlist. UPDATE and DELETE always
|
||||||
|
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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);
|
|||||||
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
|
|||||||
* 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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
* 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
|
|||||||
* 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
|
|||||||
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;
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user