diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 0a766ef..1775e77 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -919,7 +919,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 		{
 			PlanRowMark *rc = (PlanRowMark *) lfirst(l);
 			Oid			relid;
-			Relation	relation;
 			ExecRowMark *erm;
 
 			/* ignore "parent" rowmarks; they are irrelevant at runtime */
@@ -929,32 +928,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 			/* 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;
-			}
-
-			/* 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;
@@ -963,6 +937,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 			erm->strength = rc->strength;
 			erm->waitPolicy = rc->waitPolicy;
 			erm->ermActive = false;
+			erm->ermChecked = false;
 			ItemPointerSetInvalid(&(erm->curCtid));
 			erm->ermExtra = NULL;
 
@@ -2462,6 +2437,20 @@ ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
 	return aerm;
 }
 
+Relation
+ExecRowMarkGetRelation(EState *estate, ExecRowMark *erm)
+{
+	Relation relation = ExecGetRangeTableRelation(estate, erm->rti);
+
+	/* Check that relation is a legal target for marking, if we haven't */
+	if (!erm->ermChecked)
+	{
+		CheckValidRowMarkRel(relation, erm->markType);
+		erm->ermChecked = true;
+	}
+	return relation;
+}
+
 
 /*
  * EvalPlanQual logic --- recheck modified tuple(s) to see if we want to
@@ -2935,10 +2924,9 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 
 		if (erm->markType == ROW_MARK_REFERENCE)
 		{
+			Relation relation = ExecRowMarkGetRelation(epqstate->estate, erm);
 			HeapTuple	copyTuple;
 
-			Assert(erm->relation != NULL);
-
 			/* fetch the tuple's ctid */
 			datum = ExecGetJunkAttribute(epqstate->origslot,
 										 aerm->ctidAttNo,
@@ -2948,18 +2936,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 				continue;
 
 			/* fetch requests on foreign tables must be passed to their FDW */
-			if (erm->relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+			if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
 			{
 				FdwRoutine *fdwroutine;
 				bool		updated = false;
 
-				fdwroutine = GetFdwRoutineForRelation(erm->relation, false);
+				fdwroutine = GetFdwRoutineForRelation(relation, false);
 				/* this should have been checked already, but let's be safe */
 				if (fdwroutine->RefetchForeignRow == NULL)
 					ereport(ERROR,
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 							 errmsg("cannot lock rows in foreign table \"%s\"",
-									RelationGetRelationName(erm->relation))));
+									RelationGetRelationName(relation))));
 				copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate,
 														  erm,
 														  datum,
@@ -2979,7 +2967,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate)
 				Buffer		buffer;
 
 				tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
-				if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+				if (!heap_fetch(relation, SnapshotAny, &tuple, &buffer,
 								false, NULL))
 					elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
 
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 6db345a..4e161c0 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -74,6 +74,7 @@ lnext:
 	{
 		ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
 		ExecRowMark *erm = aerm->rowmark;
+		Relation relation;
 		HeapTuple  *testTuple;
 		Datum		datum;
 		bool		isNull;
@@ -122,19 +123,22 @@ lnext:
 		if (isNull)
 			elog(ERROR, "ctid is NULL");
 
+		/* access the tuple's relation */
+		relation = ExecRowMarkGetRelation(estate, erm);
+
 		/* requests for foreign tables must be passed to their FDW */
-		if (erm->relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+		if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
 		{
 			FdwRoutine *fdwroutine;
 			bool		updated = false;
 
-			fdwroutine = GetFdwRoutineForRelation(erm->relation, false);
+			fdwroutine = GetFdwRoutineForRelation(relation, false);
 			/* this should have been checked already, but let's be safe */
 			if (fdwroutine->RefetchForeignRow == NULL)
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("cannot lock rows in foreign table \"%s\"",
-								RelationGetRelationName(erm->relation))));
+								RelationGetRelationName(relation))));
 			copyTuple = fdwroutine->RefetchForeignRow(estate,
 													  erm,
 													  datum,
@@ -180,7 +184,7 @@ lnext:
 				break;
 		}
 
-		test = heap_lock_tuple(erm->relation, &tuple,
+		test = heap_lock_tuple(relation, &tuple,
 							   estate->es_output_cid,
 							   lockmode, erm->waitPolicy, true,
 							   &buffer, &hufd);
@@ -230,7 +234,7 @@ lnext:
 				}
 
 				/* updated, so fetch and lock the updated version */
-				copyTuple = EvalPlanQualFetch(estate, erm->relation,
+				copyTuple = EvalPlanQualFetch(estate, relation,
 											  lockmode, erm->waitPolicy,
 											  &hufd.ctid, hufd.xmax);
 
@@ -286,6 +290,7 @@ lnext:
 		{
 			ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
 			ExecRowMark *erm = aerm->rowmark;
+			Relation relation;
 			HeapTupleData tuple;
 			Buffer		buffer;
 
@@ -309,13 +314,16 @@ lnext:
 				continue;
 			}
 
+			/* access the tuple's relation */
+			relation = ExecRowMarkGetRelation(estate, erm);
+
 			/* foreign tables should have been fetched above */
-			Assert(erm->relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE);
+			Assert(relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE);
 			Assert(ItemPointerIsValid(&(erm->curCtid)));
 
 			/* okay, fetch the tuple */
 			tuple.t_self = erm->curCtid;
-			if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
+			if (!heap_fetch(relation, SnapshotAny, &tuple, &buffer,
 							false, NULL))
 				elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
 
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index c9ed198..4dbbb3b 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -190,6 +190,7 @@ extern void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 extern LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo);
 extern ExecRowMark *ExecFindRowMark(EState *estate, Index rti, bool missing_ok);
 extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist);
+extern Relation ExecRowMarkGetRelation(EState *estate, ExecRowMark *erm);
 extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
 			 Relation relation, Index rti, int lockmode,
 			 ItemPointer tid, TransactionId priorXmax);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 8347089..e2cd7ea 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -597,16 +597,20 @@ typedef struct EState
  * ExecRowMark -
  *	   runtime representation of FOR [KEY] UPDATE/SHARE clauses
  *
- * When doing UPDATE, DELETE, or SELECT FOR [KEY] UPDATE/SHARE, we will have an
- * ExecRowMark for each non-target relation in the query (except inheritance
- * parent RTEs, which can be ignored at runtime).  Virtual relations such as
- * subqueries-in-FROM will have an ExecRowMark with relation == NULL.  See
- * PlanRowMark for details about most of the fields.  In addition to fields
- * directly derived from PlanRowMark, we store an activity flag (to denote
- * inactive children of inheritance trees), curCtid, which is used by the
- * WHERE CURRENT OF code, and ermExtra, which is available for use by the plan
- * node that sources the relation (e.g., for a foreign table the FDW can use
- * ermExtra to hold information).
+ * When doing UPDATE, DELETE, or SELECT FOR [KEY] UPDATE/SHARE, we will 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.  In addition to fields directly
+ * derived from PlanRowMark, we store an activity flag (to denote inactive
+ * children of inheritance trees); a "checked" flag (to denote whether we've
+ * verified the relation is of appropriate type to be marked); curCtid, which
+ * is used by the WHERE CURRENT OF code; and ermExtra, which is available for
+ * use by the plan node that sources the relation (e.g., for a foreign table
+ * the FDW can use ermExtra to hold information).
+ *
+ * For performance reasons, we don't open or verify the relkind of tables
+ * that are never accessed in a query; this is important in partitioned
+ * tables with many partitions.  Use ExecRowMarkGetRelation to do that.
  *
  * 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
@@ -614,8 +618,7 @@ typedef struct EState
  */
 typedef struct ExecRowMark
 {
-	Relation	relation;		/* opened and suitably locked relation */
-	Oid			relid;			/* its OID (or InvalidOid, if subquery) */
+	Oid			relid;			/* relation's OID (InvalidOid if subquery) */
 	Index		rti;			/* its range table index */
 	Index		prti;			/* parent range table index, if child */
 	Index		rowmarkId;		/* unique identifier for resjunk columns */
@@ -623,6 +626,7 @@ typedef struct ExecRowMark
 	LockClauseStrength strength;	/* LockingClause's strength, or LCS_NONE */
 	LockWaitPolicy waitPolicy;	/* NOWAIT and SKIP LOCKED */
 	bool		ermActive;		/* is this mark relevant for current tuple? */
+	bool		ermChecked;		/* have we verified relation's relkind? */
 	ItemPointerData curCtid;	/* ctid of currently locked tuple, if any */
 	void	   *ermExtra;		/* available for use by relation source node */
 } ExecRowMark;
