Fix PlanRowMark/ExecRowMark structures to handle inheritance correctly.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 13 Jan 2011 01:47:09 +0000 (20:47 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 13 Jan 2011 01:47:09 +0000 (20:47 -0500)
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.

12 files changed:
src/backend/executor/execJunk.c
src/backend/executor/execMain.c
src/backend/executor/nodeLockRows.c
src/backend/executor/nodeModifyTable.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/preptlist.c
src/backend/optimizer/prep/prepunion.c
src/include/executor/executor.h
src/include/nodes/execnodes.h
src/include/nodes/plannodes.h

index 2fa7e68a8edde4d7465896d77b85e7b3ba75acbd..da227093f3011d902e3a6afe96b3b359583dd9a4 100644 (file)
@@ -207,10 +207,22 @@ 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);
 
index 0b6cbcc4af0484f8d07f0263c01444af577efe59..6d4a77328a4f0610c996094e6ba8639d9aceeabf 100644 (file)
@@ -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);
        }
@@ -1334,6 +1331,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)
index 1f203b4fe6be4e8f271b3fe0989198bb9c954506..8798a3b0a4d4dd9c5ebcd87e8f518de9db72e204 100644 (file)
@@ -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;
 }
 
index 969938130ec5bed7200f7f8c93abf0f1b85a2db1..f4b2b16b69e6e257f85934375a6924b5f20db0bf 100644 (file)
@@ -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
index 5f83e4fc98d80f6f26c0886c8fe17f951b710070..0b2aa3dfff8549d1eb9773dd32bf20c5d9aefc98 100644 (file)
@@ -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;
 }
index 030a675b7b6cefb14c63e5d84c5dc66de0dbd0a5..032150e1bb737edf9e367d6729545f43a1bc8bc4 100644 (file)
@@ -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
index 317a7e87f3314440676450b4a5b1eae2d3ced2e2..45ba902fbab1bf6743b50d9f694cc9c818c3199f 100644 (file)
@@ -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);
        }
index 2ea01a7f08d2479be7894b7a36e54e5ba7d30ac3..7a09d1f830999acf6266375d738a2ea156974667 100644 (file)
@@ -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;
                }
        }
 
index 3cb5ed977b05a9cdf9aca73e77a4707a2d5af0df..35f9f980e9068875fecdd0ed048becb8afb93508 100644 (file)
@@ -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);
                }
index b2424a0d087002440274d24fb28d930b301e29a3..13b6aa57bea6b97a7a92e66534806106a0cb5842 100644 (file)
@@ -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);
index 928cf4c5a555591cd6fc84ca8b8d3e8d61a9dd4c..fda44255b08dde1cdc4599b910d7c65b48cb8e27 100644 (file)
@@ -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;
 
index 79876100d5ae6a40e5d10a49c9fb01dd73223f54..e14257932e692100993b13e148241953ce34e405 100644 (file)
@@ -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;