aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-01-12 20:47:09 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2011-01-12 20:47:09 -0500
commitfed8dcdb84d255088d22efa3156a193f3399e792 (patch)
tree8262474db57695b73d2fcdb0975a5f89f5573307 /src
parenta1ed4cf6ca6ee2115d9f618ed7930a97842042a8 (diff)
downloadpostgresql-fed8dcdb84d255088d22efa3156a193f3399e792.tar.gz
postgresql-fed8dcdb84d255088d22efa3156a193f3399e792.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execJunk.c14
-rw-r--r--src/backend/executor/execMain.c107
-rw-r--r--src/backend/executor/nodeLockRows.c51
-rw-r--r--src/backend/executor/nodeModifyTable.c42
-rw-r--r--src/backend/nodes/copyfuncs.c3
-rw-r--r--src/backend/nodes/outfuncs.c3
-rw-r--r--src/backend/optimizer/plan/planner.c8
-rw-r--r--src/backend/optimizer/prep/preptlist.c17
-rw-r--r--src/backend/optimizer/prep/prepunion.c4
-rw-r--r--src/include/executor/executor.h10
-rw-r--r--src/include/nodes/execnodes.h34
-rw-r--r--src/include/nodes/plannodes.h21
12 files changed, 192 insertions, 122 deletions
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index 2fa7e68a8ed..da227093f30 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -208,9 +208,21 @@ ExecInitJunkFilterConversion(List *targetList,
AttrNumber
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;
- foreach(t, junkfilter->jf_targetList)
+ foreach(t, targetlist)
{
TargetEntry *tle = lfirst(t);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 0b6cbcc4af0..6d4a77328a4 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -728,9 +728,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
erm->prti = rc->prti;
erm->markType = rc->markType;
erm->noWait = rc->noWait;
- erm->ctidAttNo = rc->ctidAttNo;
- erm->toidAttNo = rc->toidAttNo;
- erm->wholeAttNo = rc->wholeAttNo;
ItemPointerSetInvalid(&(erm->curCtid));
estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
}
@@ -1335,6 +1332,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
* process the updated version under READ COMMITTED rules.
*
@@ -1624,11 +1686,13 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode,
/*
* EvalPlanQualInit -- initialize during creation of a plan state node
* 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
EvalPlanQualInit(EPQState *epqstate, EState *estate,
- Plan *subplan, int epqParam)
+ Plan *subplan, List *auxrowmarks, int epqParam)
{
/* Mark the EPQ state inactive */
epqstate->estate = NULL;
@@ -1636,7 +1700,7 @@ EvalPlanQualInit(EPQState *epqstate, EState *estate,
epqstate->origslot = NULL;
/* ... and remember data that EvalPlanQualBegin will need */
epqstate->plan = subplan;
- epqstate->rowMarks = NIL;
+ epqstate->arowMarks = auxrowmarks;
epqstate->epqParam = epqParam;
}
@@ -1646,25 +1710,14 @@ EvalPlanQualInit(EPQState *epqstate, EState *estate,
* We need this so that ModifyTuple can deal with multiple subplans.
*/
void
-EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan)
+EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
{
/* If we have a live EPQ query, shut it down */
EvalPlanQualEnd(epqstate);
/* And set/change the plan pointer */
epqstate->plan = subplan;
-}
-
-/*
- * 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);
+ /* The rowmarks depend on the plan, too */
+ epqstate->arowMarks = auxrowmarks;
}
/*
@@ -1714,13 +1767,17 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
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;
bool isNull;
HeapTupleData tuple;
+ if (RowMarkRequiresRowShareLock(erm->markType))
+ elog(ERROR, "EvalPlanQual doesn't support locking rowmarks");
+
/* clear any leftover test tuple for this rel */
EvalPlanQualSetTuple(epqstate, erm->rti, NULL);
@@ -1736,7 +1793,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
Oid tableoid;
datum = ExecGetJunkAttribute(epqstate->origslot,
- erm->toidAttNo,
+ aerm->toidAttNo,
&isNull);
/* non-locked rels could be on the inside of outer joins */
if (isNull)
@@ -1752,7 +1809,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* fetch the tuple's ctid */
datum = ExecGetJunkAttribute(epqstate->origslot,
- erm->ctidAttNo,
+ aerm->ctidAttNo,
&isNull);
/* non-locked rels could be on the inside of outer joins */
if (isNull)
@@ -1777,7 +1834,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
/* fetch the whole-row Var for the relation */
datum = ExecGetJunkAttribute(epqstate->origslot,
- erm->wholeAttNo,
+ aerm->wholeAttNo,
&isNull);
/* non-locked rels could be on the inside of outer joins */
if (isNull)
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 1f203b4fe6b..8798a3b0a4d 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -58,12 +58,13 @@ lnext:
/*
* Attempt to lock the source tuple(s). (Note we only have locking
- * rowmarks in lr_rowMarks.)
+ * rowmarks in lr_arowMarks.)
*/
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;
bool isNull;
HeapTupleData tuple;
@@ -84,7 +85,7 @@ lnext:
Oid tableoid;
datum = ExecGetJunkAttribute(slot,
- erm->toidAttNo,
+ aerm->toidAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
@@ -101,7 +102,7 @@ lnext:
/* fetch the tuple's ctid */
datum = ExecGetJunkAttribute(slot,
- erm->ctidAttNo,
+ aerm->ctidAttNo,
&isNull);
/* shouldn't ever get a null result... */
if (isNull)
@@ -189,9 +190,10 @@ lnext:
* so as to avoid overhead in the common case where there are no
* 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;
Buffer buffer;
@@ -251,6 +253,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
{
LockRowsState *lrstate;
Plan *outerPlan = outerPlan(node);
+ List *epq_arowmarks;
ListCell *lc;
/* check for unsupported flags */
@@ -262,7 +265,6 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
lrstate = makeNode(LockRowsState);
lrstate->ps.plan = (Plan *) node;
lrstate->ps.state = estate;
- EvalPlanQualInit(&lrstate->lr_epqstate, estate, outerPlan, node->epqParam);
/*
* Miscellaneous initialization
@@ -288,15 +290,17 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
lrstate->ps.ps_ProjInfo = NULL;
/*
- * Locate the ExecRowMark(s) that this node is responsible for. (InitPlan
- * should already have built the global list of ExecRowMarks.)
+ * Locate the ExecRowMark(s) that this node is responsible for, and
+ * 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)
{
PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
- ExecRowMark *erm = NULL;
- ListCell *lce;
+ ExecRowMark *erm;
+ ExecAuxRowMark *aerm;
Assert(IsA(rc, PlanRowMark));
@@ -304,16 +308,9 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
if (rc->isParent)
continue;
- foreach(lce, estate->es_rowMarks)
- {
- 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);
+ /* find ExecRowMark and build ExecAuxRowMark */
+ erm = ExecFindRowMark(estate, rc->rti);
+ aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
/*
* 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.
*/
if (RowMarkRequiresRowShareLock(erm->markType))
- lrstate->lr_rowMarks = lappend(lrstate->lr_rowMarks, erm);
+ lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
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;
}
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 969938130ec..f4b2b16b69e 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -701,7 +701,8 @@ ExecModifyTable(ModifyTableState *node)
estate->es_result_relation_info++;
subplanstate = node->mt_plans[node->mt_whichplan];
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;
}
else
@@ -815,10 +816,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->ps.targetlist = NIL; /* not actually used */
mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
+ mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
mtstate->mt_nplans = nplans;
mtstate->operation = operation;
- /* set up epqstate with dummy subplan pointer for the moment */
- EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, node->epqParam);
+ /* set up epqstate with dummy subplan data for the moment */
+ EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
mtstate->fireBSTriggers = true;
/* For the moment, assume our targets are exactly the global result rels */
@@ -840,11 +842,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
}
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.
*/
@@ -908,8 +905,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
foreach(l, node->rowMarks)
{
PlanRowMark *rc = (PlanRowMark *) lfirst(l);
- ExecRowMark *erm = NULL;
- ListCell *lce;
+ ExecRowMark *erm;
Assert(IsA(rc, PlanRowMark));
@@ -917,20 +913,26 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (rc->isParent)
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);
- if (erm->rti == rc->rti)
- break;
- erm = NULL;
- }
- if (erm == NULL)
- elog(ERROR, "failed to find ExecRowMark for PlanRowMark %u",
- rc->rti);
+ ExecAuxRowMark *aerm;
- 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
* if there are any junk attrs in the tlist. UPDATE and DELETE always
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5f83e4fc98d..0b2aa3dfff8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -859,9 +859,6 @@ _copyPlanRowMark(PlanRowMark *from)
COPY_SCALAR_FIELD(markType);
COPY_SCALAR_FIELD(noWait);
COPY_SCALAR_FIELD(isParent);
- COPY_SCALAR_FIELD(ctidAttNo);
- COPY_SCALAR_FIELD(toidAttNo);
- COPY_SCALAR_FIELD(wholeAttNo);
return newnode;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 030a675b7b6..032150e1bb7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -759,9 +759,6 @@ _outPlanRowMark(StringInfo str, PlanRowMark *node)
WRITE_ENUM_FIELD(markType, RowMarkType);
WRITE_BOOL_FIELD(noWait);
WRITE_BOOL_FIELD(isParent);
- WRITE_INT_FIELD(ctidAttNo);
- WRITE_INT_FIELD(toidAttNo);
- WRITE_INT_FIELD(wholeAttNo);
}
static void
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 317a7e87f33..45ba902fbab 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1858,10 +1858,6 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->markType = ROW_MARK_SHARE;
newrc->noWait = rc->noWait;
newrc->isParent = false;
- /* attnos will be assigned in preprocess_targetlist */
- newrc->ctidAttNo = InvalidAttrNumber;
- newrc->toidAttNo = InvalidAttrNumber;
- newrc->wholeAttNo = InvalidAttrNumber;
prowmarks = lappend(prowmarks, newrc);
}
@@ -1888,10 +1884,6 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->markType = ROW_MARK_COPY;
newrc->noWait = false; /* doesn't matter */
newrc->isParent = false;
- /* attnos will be assigned in preprocess_targetlist */
- newrc->ctidAttNo = InvalidAttrNumber;
- newrc->toidAttNo = InvalidAttrNumber;
- newrc->wholeAttNo = InvalidAttrNumber;
prowmarks = lappend(prowmarks, newrc);
}
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 2ea01a7f08d..7a09d1f8309 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -111,8 +111,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
/*
* Add necessary junk columns for rowmarked rels. These values are needed
* for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
- * rechecking. While we are at it, store these junk attnos in the
- * PlanRowMark list so that we don't have to redetermine them at runtime.
+ * rechecking. See comments for PlanRowMark in plannodes.h.
*/
foreach(lc, root->rowMarks)
{
@@ -121,18 +120,9 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
char resname[32];
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)
- {
- 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;
- }
if (rc->markType != ROW_MARK_COPY)
{
@@ -148,7 +138,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
pstrdup(resname),
true);
tlist = lappend(tlist, tle);
- rc->ctidAttNo = tle->resno;
/* if parent of inheritance tree, need the tableoid too */
if (rc->isParent)
@@ -164,7 +153,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
pstrdup(resname),
true);
tlist = lappend(tlist, tle);
- rc->toidAttNo = tle->resno;
}
}
else
@@ -179,7 +167,6 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
pstrdup(resname),
true);
tlist = lappend(tlist, tle);
- rc->wholeAttNo = tle->resno;
}
}
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 3cb5ed977b0..35f9f980e90 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1291,10 +1291,6 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
newrc->markType = oldrc->markType;
newrc->noWait = oldrc->noWait;
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);
}
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index b2424a0d087..13b6aa57bea 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -138,6 +138,8 @@ extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
TupleTableSlot *slot);
extern AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter,
const char *attrName);
+extern AttrNumber ExecFindJunkAttributeInTlist(List *targetlist,
+ const char *attrName);
extern Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno,
bool *isNull);
extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter,
@@ -166,15 +168,17 @@ extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
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,
Relation relation, Index rti,
ItemPointer tid, TransactionId priorXmax);
extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation,
int lockmode, ItemPointer tid, TransactionId priorXmax);
extern void EvalPlanQualInit(EPQState *epqstate, EState *estate,
- Plan *subplan, int epqParam);
-extern void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan);
-extern void EvalPlanQualAddRowMark(EPQState *epqstate, ExecRowMark *erm);
+ Plan *subplan, List *auxrowmarks, int epqParam);
+extern void EvalPlanQualSetPlan(EPQState *epqstate,
+ Plan *subplan, List *auxrowmarks);
extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti,
HeapTuple tuple);
extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 928cf4c5a55..fda44255b08 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -407,12 +407,10 @@ typedef struct EState
* When doing UPDATE, DELETE, or SELECT FOR UPDATE/SHARE, we should have an
* ExecRowMark for each non-target relation in the query (except inheritance
* 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
- * 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.
+ * EState->es_rowMarks is a list of these structs.
*/
typedef struct ExecRowMark
{
@@ -421,11 +419,28 @@ typedef struct ExecRowMark
Index prti; /* parent range table index, if child */
RowMarkType markType; /* see enum in nodes/plannodes.h */
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 toidAttNo; /* resno of tableoid junk attribute, if any */
AttrNumber wholeAttNo; /* resno of whole-row junk attribute, if any */
- ItemPointerData curCtid; /* ctid of currently locked tuple, if any */
-} ExecRowMark;
+} ExecAuxRowMark;
/* ----------------------------------------------------------------
@@ -1002,7 +1017,7 @@ typedef struct EPQState
PlanState *planstate; /* plan state tree ready to be executed */
TupleTableSlot *origslot; /* original output tuple to be rechecked */
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 */
} EPQState;
@@ -1030,6 +1045,7 @@ typedef struct ModifyTableState
PlanState **mt_plans; /* subplans (one per target rel) */
int mt_nplans; /* number of plans in the array */
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 */
bool fireBSTriggers; /* do we need to fire stmt triggers? */
} ModifyTableState;
@@ -1706,7 +1722,7 @@ typedef struct SetOpState
typedef struct LockRowsState
{
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 */
} LockRowsState;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 79876100d5a..e14257932e6 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -158,6 +158,9 @@ typedef struct Result
* ModifyTable node -
* Apply rows produced by subplan(s) to result table(s),
* 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
@@ -690,9 +693,18 @@ typedef enum RowMarkType
* prti == parent's RT index, and can therefore be recognized as children by
* the fact that prti != rti.
*
- * The AttrNumbers are filled in during preprocess_targetlist. We use
- * different subsets of them for plain relations, inheritance children,
- * and non-table relations.
+ * The planner also adds resjunk output columns to the plan that carry
+ * information sufficient to identify the locked or fetched rows. For
+ * 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
{
@@ -702,9 +714,6 @@ typedef struct PlanRowMark
RowMarkType markType; /* see enum above */
bool noWait; /* NOWAIT option */
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;