}
 
    /*
-    * Next, build the ExecRowMark list from the PlanRowMark(s), if any.
+    * Next, build the ExecRowMark array from the PlanRowMark(s), if any.
     */
-   estate->es_rowMarks = NIL;
-   foreach(l, plannedstmt->rowMarks)
+   if (plannedstmt->rowMarks)
    {
-       PlanRowMark *rc = (PlanRowMark *) lfirst(l);
-       Oid         relid;
-       Relation    relation;
-       ExecRowMark *erm;
+       estate->es_rowmarks = (ExecRowMark **)
+           palloc0(estate->es_range_table_size * sizeof(ExecRowMark *));
+       foreach(l, plannedstmt->rowMarks)
+       {
+           PlanRowMark *rc = (PlanRowMark *) lfirst(l);
+           Oid         relid;
+           Relation    relation;
+           ExecRowMark *erm;
 
-       /* ignore "parent" rowmarks; they are irrelevant at runtime */
-       if (rc->isParent)
-           continue;
+           /* ignore "parent" rowmarks; they are irrelevant at runtime */
+           if (rc->isParent)
+               continue;
 
-       /* get relation's OID (will produce InvalidOid if subquery) */
-       relid = exec_rt_fetch(rc->rti, estate)->relid;
+           /* get relation's OID (will produce InvalidOid if subquery) */
+           relid = exec_rt_fetch(rc->rti, estate)->relid;
 
-       /* open relation, if we need to access it for this mark type */
-       switch (rc->markType)
-       {
-           case ROW_MARK_EXCLUSIVE:
-           case ROW_MARK_NOKEYEXCLUSIVE:
-           case ROW_MARK_SHARE:
-           case ROW_MARK_KEYSHARE:
-           case ROW_MARK_REFERENCE:
-               relation = ExecGetRangeTableRelation(estate, rc->rti);
-               break;
-           case ROW_MARK_COPY:
-               /* no physical table access is required */
-               relation = NULL;
-               break;
-           default:
-               elog(ERROR, "unrecognized markType: %d", rc->markType);
-               relation = NULL;    /* keep compiler quiet */
-               break;
-       }
+           /* open relation, if we need to access it for this mark type */
+           switch (rc->markType)
+           {
+               case ROW_MARK_EXCLUSIVE:
+               case ROW_MARK_NOKEYEXCLUSIVE:
+               case ROW_MARK_SHARE:
+               case ROW_MARK_KEYSHARE:
+               case ROW_MARK_REFERENCE:
+                   relation = ExecGetRangeTableRelation(estate, rc->rti);
+                   break;
+               case ROW_MARK_COPY:
+                   /* no physical table access is required */
+                   relation = NULL;
+                   break;
+               default:
+                   elog(ERROR, "unrecognized markType: %d", rc->markType);
+                   relation = NULL;    /* keep compiler quiet */
+                   break;
+           }
 
-       /* Check that relation is a legal target for marking */
-       if (relation)
-           CheckValidRowMarkRel(relation, rc->markType);
-
-       erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
-       erm->relation = relation;
-       erm->relid = relid;
-       erm->rti = rc->rti;
-       erm->prti = rc->prti;
-       erm->rowmarkId = rc->rowmarkId;
-       erm->markType = rc->markType;
-       erm->strength = rc->strength;
-       erm->waitPolicy = rc->waitPolicy;
-       erm->ermActive = false;
-       ItemPointerSetInvalid(&(erm->curCtid));
-       erm->ermExtra = NULL;
-
-       estate->es_rowMarks = lappend(estate->es_rowMarks, erm);
+           /* Check that relation is a legal target for marking */
+           if (relation)
+               CheckValidRowMarkRel(relation, rc->markType);
+
+           erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
+           erm->relation = relation;
+           erm->relid = relid;
+           erm->rti = rc->rti;
+           erm->prti = rc->prti;
+           erm->rowmarkId = rc->rowmarkId;
+           erm->markType = rc->markType;
+           erm->strength = rc->strength;
+           erm->waitPolicy = rc->waitPolicy;
+           erm->ermActive = false;
+           ItemPointerSetInvalid(&(erm->curCtid));
+           erm->ermExtra = NULL;
+
+           Assert(erm->rti > 0 && erm->rti <= estate->es_range_table_size &&
+                  estate->es_rowmarks[erm->rti - 1] == NULL);
+
+           estate->es_rowmarks[erm->rti - 1] = erm;
+       }
    }
 
    /*
 ExecRowMark *
 ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
 {
-   ListCell   *lc;
-
-   foreach(lc, estate->es_rowMarks)
+   if (rti > 0 && rti <= estate->es_range_table_size &&
+       estate->es_rowmarks != NULL)
    {
-       ExecRowMark *erm = (ExecRowMark *) lfirst(lc);
+       ExecRowMark *erm = estate->es_rowmarks[rti - 1];
 
-       if (erm->rti == rti)
+       if (erm)
            return erm;
    }
    if (!missing_ok)
    estate->es_range_table_array = parentestate->es_range_table_array;
    estate->es_range_table_size = parentestate->es_range_table_size;
    estate->es_relations = parentestate->es_relations;
+   estate->es_rowmarks = parentestate->es_rowmarks;
    estate->es_plannedstmt = parentestate->es_plannedstmt;
    estate->es_junkFilter = parentestate->es_junkFilter;
    estate->es_output_cid = parentestate->es_output_cid;
    }
    /* es_result_relation_info must NOT be copied */
    /* es_trig_target_relations must NOT be copied */
-   estate->es_rowMarks = parentestate->es_rowMarks;
    estate->es_top_eflags = parentestate->es_top_eflags;
    estate->es_instrument = parentestate->es_instrument;
    /* es_auxmodifytables must NOT be copied */
 
 
 struct PlanState;              /* forward references in this file */
 struct ParallelHashJoinState;
+struct ExecRowMark;
 struct ExprState;
 struct ExprContext;
 struct RangeTblEntry;          /* avoid including parsenodes.h here */
    Index       es_range_table_size;    /* size of the range table arrays */
    Relation   *es_relations;   /* Array of per-range-table-entry Relation
                                 * pointers, or NULL if not yet opened */
+   struct ExecRowMark **es_rowmarks;   /* Array of per-range-table-entry
+                                        * ExecRowMarks, or NULL if none */
    PlannedStmt *es_plannedstmt;    /* link to top of plan tree */
    const char *es_sourceText;  /* Source text from QueryDesc */
 
 
    List       *es_tupleTable;  /* List of TupleTableSlots */
 
-   List       *es_rowMarks;    /* List of ExecRowMarks */
-
    uint64      es_processed;   /* # of tuples processed */
    Oid         es_lastoid;     /* last oid processed (by INSERT) */
 
  * node that sources the relation (e.g., for a foreign table the FDW can use
  * ermExtra to hold information).
  *
- * EState->es_rowMarks is a list of these structs.
+ * EState->es_rowmarks is an array of these structs, indexed by RT index,
+ * with NULLs for irrelevant RT indexes.  es_rowmarks itself is NULL if
+ * there are no rowmarks.
  */
 typedef struct ExecRowMark
 {
  *    additional runtime representation of FOR [KEY] 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,
+ * 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
  */
 typedef struct ExecAuxRowMark
 {
-   ExecRowMark *rowmark;       /* related entry in es_rowMarks */
+   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 */