diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 3dd1bab2a2..d20c75367c 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -2050,7 +2050,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate,
 		 * Vars contained in those expressions.
 		 */
 		if (plan && plan->operation == CMD_UPDATE &&
-			resultRelation == plan->nominalRelation)
+			resultRelation == plan->targetRelation)
 			resultRelation = mtstate->resultRelInfo[0].ri_RangeTableIndex;
 	}
 
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 2be2d340ca..57efb20d20 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -837,7 +837,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
 		List	   *attnums;
 		ListCell   *cur;
 		RangeTblEntry *rte;
-		int			lockmode = is_from ? RowExclusiveLock : AccessShareLock;
+		LOCKMODE	lockmode = is_from ? RowExclusiveLock : AccessShareLock;
 
 		Assert(!stmt->query);
 
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index ed6afe79a9..5ca7f69830 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -929,7 +929,7 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 			break;
 		case T_ModifyTable:
 			*rels_used = bms_add_member(*rels_used,
-										((ModifyTable *) plan)->nominalRelation);
+										((ModifyTable *) plan)->targetRelation);
 			if (((ModifyTable *) plan)->exclRelRTI)
 				*rels_used = bms_add_member(*rels_used,
 											((ModifyTable *) plan)->exclRelRTI);
@@ -2924,7 +2924,7 @@ ExplainScanTarget(Scan *plan, ExplainState *es)
 static void
 ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
 {
-	ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
+	ExplainTargetRel((Plan *) plan, plan->targetRelation, es);
 }
 
 /*
@@ -3088,7 +3088,7 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
 	/* Should we explicitly label target relations? */
 	labeltargets = (mtstate->mt_nplans > 1 ||
 					(mtstate->mt_nplans == 1 &&
-					 mtstate->resultRelInfo->ri_RangeTableIndex != node->nominalRelation));
+					 mtstate->resultRelInfo->ri_RangeTableIndex != node->targetRelation));
 
 	if (labeltargets)
 		ExplainOpenGroup("Target Tables", "Target Tables", false, es);
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 09a6a7f9d3..2949b4e6d7 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -568,7 +568,7 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
 
 			/* Add this rel to the parsestate's rangetable, for dependencies */
 			addRangeTableEntryForRelation(qual_pstate, rel, NULL, false, false,
-										  NoLock);
+										  AccessExclusiveLock);
 
 			qual_parse_rtable = qual_pstate->p_rtable;
 			free_parsestate(qual_pstate);
@@ -591,7 +591,7 @@ RemoveRoleFromObjectPolicy(Oid roleid, Oid classid, Oid policy_id)
 
 			/* Add this rel to the parsestate's rangetable, for dependencies */
 			addRangeTableEntryForRelation(with_check_pstate, rel, NULL, false,
-										  false, NoLock);
+										  false, AccessExclusiveLock);
 
 			with_check_parse_rtable = with_check_pstate->p_rtable;
 			free_parsestate(with_check_pstate);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f5b9d768e8..5afa54389a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -13632,7 +13632,8 @@ transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
 	 * rangetable entry.  We need a ParseState for transformExpr.
 	 */
 	pstate = make_parsestate(NULL);
-	rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true, NoLock);
+	rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true,
+										NoLock);
 	addRTEtoQuery(pstate, rte, true, true, true);
 
 	/* take care of any partition expressions */
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index f673029ed1..f0d45844c1 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -579,11 +579,13 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 		 */
 		rte = addRangeTableEntryForRelation(pstate, rel,
 											makeAlias("old", NIL),
-											false, false, NoLock);
+											false, false,
+											ShareRowExclusiveLock);
 		addRTEtoQuery(pstate, rte, false, true, true);
 		rte = addRangeTableEntryForRelation(pstate, rel,
 											makeAlias("new", NIL),
-											false, false, NoLock);
+											false, false,
+											ShareRowExclusiveLock);
 		addRTEtoQuery(pstate, rte, false, true, true);
 
 		/* Transform expression.  Copy to be sure we don't modify original */
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 0d7cd552eb..30df410e0e 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -267,8 +267,8 @@ This is a sketch of control flow for full query processing:
 
 Per above comments, it's not really critical for ExecEndNode to free any
 memory; it'll all go away in FreeExecutorState anyway.  However, we do need to
-be careful to close relations, drop buffer pins, etc, so we do need to scan
-the plan state tree to find these sorts of resources.
+be careful to drop buffer pins, etc, so we do need to scan the plan state tree
+to find these sorts of resources.
 
 
 The executor can also be used to evaluate simple expressions without any Plan
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 1be4dabb53..2ca25d857d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -837,25 +837,22 @@ InitPlan(QueryDesc *queryDesc, int eflags)
 	 */
 	ExecCheckRTPerms(rangeTable, true);
 
-	/*
-	 * initialize the node's execution state
-	 */
 	ExecInitRangeTable(estate, rangeTable);
 
 	/*
-	 * Just initialize 0-filled array of Relation pointers.  Actual values are
-	 * filled later as the plan is initialized.
+	 * Allocate an array to store open Relations of each rangeTable item.  We
+	 * 0 fill this for now.  The Relations are only opened and stored here the
+	 * first time they're required during node initialization.
 	 */
 	estate->es_relations = (Relation *) palloc0(estate->es_range_table_size *
 												sizeof(Relation));
-	estate->es_plannedstmt = plannedstmt;
-
 	/*
-	 * initialize result relation stuff, and open/lock the result rels.
-	 *
-	 * We must do this before initializing the plan tree, else we might try to
-	 * do a lock upgrade if a result rel is also a source rel.
+	 * initialize the node's execution state
 	 */
+	estate->es_range_table = rangeTable;
+	estate->es_plannedstmt = plannedstmt;
+
+	/* Allocate memory required for result relations */
 	if (plannedstmt->resultRelations)
 	{
 		int		numResultRelations = list_length(plannedstmt->resultRelations);
@@ -1496,8 +1493,10 @@ ExecEndPlan(PlanState *planstate, EState *estate)
 
 	/* Close range table relations. */
 	for (i = 0; i < estate->es_range_table_size; i++)
+	{
 		if (estate->es_relations[i])
 			heap_close(estate->es_relations[i], NoLock);
+	}
 
 	/*
 	 * shut down the node-type-specific query processing
@@ -2270,7 +2269,11 @@ ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
 }
 
 /*
- * ExecBuildRowMark -- create ExecRowMark struct for given PlanRowMark
+ * ExecBuildRowMark -- create and return an ExecRowMark struct for the given
+ * PlanRowMark.
+ *
+ * The created ExecRowMark is also added to the list of rowmarks in the given
+ * EState.
  */
 ExecRowMark *
 ExecBuildRowMark(EState *estate, PlanRowMark *rc)
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 7e224b23fe..7fb34d2173 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -379,7 +379,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate,
 	leaf_part_rri = makeNode(ResultRelInfo);
 	InitResultRelInfo(leaf_part_rri,
 					  partrel,
-					  node ? node->nominalRelation : 1,
+					  node ? node->targetRelation : 1,
 					  rootrel,
 					  estate->es_instrument);
 
@@ -1417,9 +1417,6 @@ adjust_partition_tlist(List *tlist, TupleConversionMap *map)
  *		functions.  Details stored include how to map the partition index
  *		returned by the partition pruning code into subplan indexes.
  *
- * ExecDestroyPartitionPruneState:
- *		Deletes a PartitionPruneState. Must be called during executor shutdown.
- *
  * ExecFindInitialMatchingSubPlans:
  *		Returns indexes of matching subplans.  Partition pruning is attempted
  *		without any evaluation of expressions containing PARAM_EXEC Params.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index abee92ee59..ab46fd425d 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -25,7 +25,6 @@
  *		etc
  *
  *		ExecOpenScanRelation	Common code for scan node init routines.
- *		ExecCloseScanRelation
  *
  *		executor_errposition	Report syntactic position of an error.
  *
@@ -35,6 +34,8 @@
  *		GetAttributeByName		Runtime extraction of columns from tuples.
  *		GetAttributeByNum
  *
+ *		ExecInitRangeTable		Build an array from the range table list to
+ *								allow faster lookups by relid.
  *	 NOTES
  *		This file has traditionally been the place to stick misc.
  *		executor support stuff that doesn't really go anyplace else.
@@ -997,10 +998,10 @@ ExecInitRangeTable(EState *estate, List *rangeTable)
 			 * meaning the upstream code already acquired the needed locks.
 			 */
 			Assert(rte->lockmode != NoLock);
-			if (LockRelationOid(rte->relid, rte->lockmode) &&
-				!IsParallelWorker())
-				elog(NOTICE, "InitPlan: lock on \"%s\" not already taken",
-					 get_rel_name(rte->relid));
+			if (!IsParallelWorker() &&
+				!CheckRelationLockedByUs(rte->relid, rte->lockmode))
+				elog(WARNING, "InitPlan: lock on \"%s\" not already taken",
+					get_rel_name(rte->relid));
 		}
 	}
 #endif
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index 9e7c5e94cf..ff9606342b 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -424,7 +424,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
 		/* safety check on size of lr_curtuples array */
 		Assert(rc->rti > 0 && rc->rti <= lrstate->lr_ntables);
 
-		/* find ExecRowMark and build ExecAuxRowMark */
+		/* build the ExecRowMark and ExecAuxRowMark */
 		erm = ExecBuildRowMark(estate, rc);
 		aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 082666c76c..c837d36a87 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2260,11 +2260,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 			node->rootResultRelIndex;
 
 		Assert(node->partitionedTarget);
-		/* nominalRelation is the index of the target partitioned table. */
-		rel = ExecRangeTableRelation(estate, node->nominalRelation);
+		rel = ExecRangeTableRelation(estate, node->targetRelation);
 		InitResultRelInfo(mtstate->rootResultRelInfo,
 						  rel,
-						  node->nominalRelation,
+						  node->targetRelation,
 						  NULL,
 						  estate->es_instrument);
 	}
@@ -2554,7 +2553,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		if (rc->isParent)
 			continue;
 
-		/* find ExecRowMark (same for all subplans) */
+		/* build the ExecRowMark (same for all subplans) */
 		erm = ExecBuildRowMark(estate, rc);
 
 		/* build ExecAuxRowMark for each subplan */
@@ -2711,9 +2710,7 @@ ExecEndModifyTable(ModifyTableState *node)
 {
 	int			i;
 
-	/*
-	 * close the result relation(s) if any, but hold locks until xact commit.
-	 */
+	/* Perform cleanup. */
 	for (i = 0; i < node->mt_nplans; i++)
 	{
 		ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
@@ -2725,9 +2722,11 @@ ExecEndModifyTable(ModifyTableState *node)
 			resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
 														   resultRelInfo);
 
-		/* Close indices and then the relation itself */
+		/*
+		 * Close the ResultRelInfo's indexes. The relation itself
+		 * is handled by the executor cleanup code.
+		 */
 		ExecCloseIndices(resultRelInfo);
-		resultRelInfo++;
 	}
 
 	/* Close all the partitioned tables, leaf partitions, and their indices */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index cde0cd7c16..dd76284676 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -202,7 +202,7 @@ _copyModifyTable(const ModifyTable *from)
 	 */
 	COPY_SCALAR_FIELD(operation);
 	COPY_SCALAR_FIELD(canSetTag);
-	COPY_SCALAR_FIELD(nominalRelation);
+	COPY_SCALAR_FIELD(targetRelation);
 	COPY_SCALAR_FIELD(partitionedTarget);
 	COPY_SCALAR_FIELD(partColsUpdated);
 	COPY_NODE_FIELD(resultRelations);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 683a7ba94e..6aefdd8bc4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -374,7 +374,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
 
 	WRITE_ENUM_FIELD(operation, CmdType);
 	WRITE_BOOL_FIELD(canSetTag);
-	WRITE_UINT_FIELD(nominalRelation);
+	WRITE_UINT_FIELD(targetRelation);
 	WRITE_BOOL_FIELD(partitionedTarget);
 	WRITE_BOOL_FIELD(partColsUpdated);
 	WRITE_NODE_FIELD(resultRelations);
@@ -2175,7 +2175,7 @@ _outModifyTablePath(StringInfo str, const ModifyTablePath *node)
 
 	WRITE_ENUM_FIELD(operation, CmdType);
 	WRITE_BOOL_FIELD(canSetTag);
-	WRITE_UINT_FIELD(nominalRelation);
+	WRITE_UINT_FIELD(targetRelation);
 	WRITE_BOOL_FIELD(partitionedTarget);
 	WRITE_BOOL_FIELD(partColsUpdated);
 	WRITE_NODE_FIELD(resultRelations);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 342b271ea5..30609c96e6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1596,7 +1596,7 @@ _readModifyTable(void)
 
 	READ_ENUM_FIELD(operation, CmdType);
 	READ_BOOL_FIELD(canSetTag);
-	READ_UINT_FIELD(nominalRelation);
+	READ_UINT_FIELD(targetRelation);
 	READ_BOOL_FIELD(partitionedTarget);
 	READ_BOOL_FIELD(partColsUpdated);
 	READ_NODE_FIELD(resultRelations);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index becc5ef75d..8f20e54d9e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -278,7 +278,7 @@ static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
 static ProjectSet *make_project_set(List *tlist, Plan *subplan);
 static ModifyTable *make_modifytable(PlannerInfo *root,
 				 CmdType operation, bool canSetTag,
-				 Index nominalRelation, bool partitionedTarget,
+				 Index targetRelation, bool partitionedTarget,
 				 bool partColsUpdated,
 				 List *resultRelations, List *subplans, List *subroots,
 				 List *withCheckOptionLists, List *returningLists,
@@ -2405,7 +2405,7 @@ create_modifytable_plan(PlannerInfo *root, ModifyTablePath *best_path)
 	plan = make_modifytable(root,
 							best_path->operation,
 							best_path->canSetTag,
-							best_path->nominalRelation,
+							best_path->targetRelation,
 							best_path->partitionedTarget,
 							best_path->partColsUpdated,
 							best_path->resultRelations,
@@ -6481,7 +6481,7 @@ make_project_set(List *tlist,
 static ModifyTable *
 make_modifytable(PlannerInfo *root,
 				 CmdType operation, bool canSetTag,
-				 Index nominalRelation, bool partitionedTarget,
+				 Index targetRelation, bool partitionedTarget,
 				 bool partColsUpdated,
 				 List *resultRelations, List *subplans, List *subroots,
 				 List *withCheckOptionLists, List *returningLists,
@@ -6509,7 +6509,7 @@ make_modifytable(PlannerInfo *root,
 
 	node->operation = operation;
 	node->canSetTag = canSetTag;
-	node->nominalRelation = nominalRelation;
+	node->targetRelation = targetRelation;
 	node->partitionedTarget = partitionedTarget;
 	node->partColsUpdated = partColsUpdated;
 	node->resultRelations = resultRelations;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index cd46849f26..074ec9bf55 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -248,7 +248,7 @@ static bool group_by_has_partkey(RelOptInfo *input_rel,
 					 List *targetList,
 					 List *groupClause);
 static int	common_prefix_cmp(const void *a, const void *b);
-static List *get_unpruned_rowmarks(PlannerInfo *root);
+static List *get_nondummy_rowmarks(PlannerInfo *root);
 
 /*****************************************************************************
  *
@@ -1167,7 +1167,7 @@ inheritance_planner(PlannerInfo *root)
 	int			top_parentRTindex = parse->resultRelation;
 	Bitmapset  *subqueryRTindexes;
 	Bitmapset  *modifiableARIindexes;
-	int			nominalRelation = -1;
+	int			targetRelation = -1;
 	List	   *final_rtable = NIL;
 	int			save_rel_array_size = 0;
 	RelOptInfo **save_rel_array = NULL;
@@ -1245,19 +1245,13 @@ inheritance_planner(PlannerInfo *root)
 	}
 
 	/*
-	 * If the parent RTE is a partitioned table, we should use that as the
-	 * nominal relation, because the RTEs added for partitioned tables
-	 * (including the root parent) as child members of the inheritance set do
-	 * not appear anywhere else in the plan.  The situation is exactly the
-	 * opposite in the case of non-partitioned inheritance parent as described
-	 * below. For the same reason, collect the list of descendant partitioned
-	 * tables to be saved in ModifyTable node, so that executor can lock those
-	 * as well.
+	 * If the parent RTE is a partitioned table, set that as the target
+	 * relation and mark that we're working with a partitioned target.
 	 */
 	parent_rte = rt_fetch(top_parentRTindex, root->parse->rtable);
 	if (parent_rte->relkind == RELKIND_PARTITIONED_TABLE)
 	{
-		nominalRelation = top_parentRTindex;
+		targetRelation = top_parentRTindex;
 		partitionedTarget = true;
 	}
 
@@ -1355,17 +1349,17 @@ inheritance_planner(PlannerInfo *root)
 		}
 
 		/*
-		 * Set the nominal target relation of the ModifyTable node if not
-		 * already done.  We use the inheritance parent RTE as the nominal
-		 * target relation if it's a partitioned table (see just above this
-		 * loop).  In the non-partitioned parent case, we'll use the first
-		 * child relation (even if it's excluded) as the nominal target
-		 * relation.  Because of the way expand_inherited_rtentry works, the
-		 * latter should be the RTE representing the parent table in its role
-		 * as a simple member of the inheritance set.
+		 * Set the target relation of the ModifyTable node if not already
+		 * done.  We use the inheritance parent RTE as the  target relation
+		 * if it's a partitioned table (see just above this loop).  In the
+		 * non-partitioned parent case, we'll use the first child relation
+		 * (even if it's excluded) as the target relation.  Because of the way
+		 * expand_inherited_rtentry works, the latter should be the RTE
+		 * representing the parent table in its role as a simple member of
+		 * the inheritance set.
 		 *
 		 * It would be logically cleaner to *always* use the inheritance
-		 * parent RTE as the nominal relation; but that RTE is not otherwise
+		 * parent RTE as the target relation; but that RTE is not otherwise
 		 * referenced in the plan in the non-partitioned inheritance case.
 		 * Instead the duplicate child RTE created by expand_inherited_rtentry
 		 * is used elsewhere in the plan, so using the original parent RTE
@@ -1375,8 +1369,8 @@ inheritance_planner(PlannerInfo *root)
 		 * duplicate child RTE added for the parent does not appear anywhere
 		 * else in the plan tree.
 		 */
-		if (nominalRelation < 0)
-			nominalRelation = appinfo->child_relid;
+		if (targetRelation < 0)
+			targetRelation = appinfo->child_relid;
 
 		/*
 		 * The rowMarks list might contain references to subquery RTEs, so
@@ -1601,14 +1595,14 @@ inheritance_planner(PlannerInfo *root)
 	if (parse->rowMarks)
 		rowMarks = NIL;
 	else
-		rowMarks = get_unpruned_rowmarks(root);
+		rowMarks = get_nondummy_rowmarks(root);
 
 	/* Create Path representing a ModifyTable to do the UPDATE/DELETE work */
 	add_path(final_rel, (Path *)
 			 create_modifytable_path(root, final_rel,
 									 parse->commandType,
 									 parse->canSetTag,
-									 nominalRelation,
+									 targetRelation,
 									 partitionedTarget,
 									 root->partColsUpdated,
 									 resultRelations,
@@ -2131,7 +2125,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		 */
 		if (parse->rowMarks)
 			path = (Path *) create_lockrows_path(root, final_rel, path,
-												 get_unpruned_rowmarks(root),
+												 get_nondummy_rowmarks(root),
 												 SS_assign_special_param(root));
 
 		/*
@@ -2177,14 +2171,14 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 			if (parse->rowMarks)
 				rowMarks = NIL;
 			else
-				rowMarks = get_unpruned_rowmarks(root);
+				rowMarks = get_nondummy_rowmarks(root);
 
 			path = (Path *)
 				create_modifytable_path(root, final_rel,
 										parse->commandType,
 										parse->canSetTag,
 										parse->resultRelation,
-										0,
+										false,
 										false,
 										list_make1_int(parse->resultRelation),
 										list_make1(path),
@@ -7225,11 +7219,11 @@ group_by_has_partkey(RelOptInfo *input_rel,
 }
 
 /*
- * get_unpruned_rowmarks
- *		Return a list of PlanRowMark's that reference unpruned relations
+ * get_nondummy_rowmarks
+ *		Return a list of PlanRowMark's that reference non-dummy relations
  */
 static List *
-get_unpruned_rowmarks(PlannerInfo *root)
+get_nondummy_rowmarks(PlannerInfo *root)
 {
 	List *rowMarks = NIL;
 	ListCell *lc;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 72ad161e57..ee01ef82d2 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -827,7 +827,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 						fix_scan_list(root, splan->exclRelTlist, rtoffset);
 				}
 
-				splan->nominalRelation += rtoffset;
+				splan->targetRelation += rtoffset;
 				splan->exclRelRTI += rtoffset;
 
 				foreach(l, splan->resultRelations)
@@ -870,7 +870,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 						list_length(root->glob->rootResultRelations);
 					root->glob->rootResultRelations =
 						lappend_int(root->glob->rootResultRelations,
-									splan->nominalRelation);
+									splan->targetRelation);
 				}
 			}
 			break;
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 28f90a2a04..73eca1d4d0 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -3291,8 +3291,8 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
  * 'rel' is the parent relation associated with the result
  * 'operation' is the operation type
  * 'canSetTag' is true if we set the command tag/es_processed
- * 'nominalRelation' is the parent RT index for use of EXPLAIN
- * 'rootRelation' is the RT index of the root partitioned table
+ * 'targetRelation' is the target table's RT index
+ * 'partitionedTarget' true if 'targetRelation' is a partitioned table
  * 'partColsUpdated' is true if any partitioning columns are being updated,
  *		either from the target relation or a descendent partitioned table.
  * 'resultRelations' is an integer list of actual RT indexes of target rel(s)
@@ -3307,7 +3307,7 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
 ModifyTablePath *
 create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
 						CmdType operation, bool canSetTag,
-						Index nominalRelation, bool partitionedTarget,
+						Index targetRelation, bool partitionedTarget,
 						bool partColsUpdated,
 						List *resultRelations, List *subpaths,
 						List *subroots,
@@ -3374,7 +3374,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
 
 	pathnode->operation = operation;
 	pathnode->canSetTag = canSetTag;
-	pathnode->nominalRelation = nominalRelation;
+	pathnode->targetRelation = targetRelation;
 	pathnode->partitionedTarget = partitionedTarget;
 	pathnode->partColsUpdated = partColsUpdated;
 	pathnode->resultRelations = resultRelations;
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 53b2832215..a9cfd1f67b 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -28,6 +28,7 @@
 #include "parser/parse_enr.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_type.h"
+#include "storage/lmgr.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
@@ -1281,6 +1282,11 @@ addRangeTableEntryForRelation(ParseState *pstate,
 
 	Assert(pstate != NULL);
 
+#ifdef USE_ASSERT_CHECKING
+	if (lockmode != NoLock && !CheckRelationLockedByUs(RelationGetRelid(rel), lockmode))
+		elog(WARNING, "lock on \"%s\" is not held", RelationGetRelationName(rel));
+#endif
+
 	rte->rtekind = RTE_RELATION;
 	rte->alias = alias;
 	rte->relid = RelationGetRelid(rel);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 14adac8312..5df0348f1c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -2638,11 +2638,11 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
 	oldrte = addRangeTableEntryForRelation(pstate, rel,
 										   makeAlias("old", NIL),
 										   false, false,
-										   AccessShareLock);
+										   AccessExclusiveLock);
 	newrte = addRangeTableEntryForRelation(pstate, rel,
 										   makeAlias("new", NIL),
 										   false, false,
-										   AccessShareLock);
+										   AccessExclusiveLock);
 	/* Must override addRangeTableEntry's default access-check flags */
 	oldrte->requiredPerms = 0;
 	newrte->requiredPerms = 0;
@@ -2738,11 +2738,11 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
 			oldrte = addRangeTableEntryForRelation(sub_pstate, rel,
 												   makeAlias("old", NIL),
 												   false, false,
-												   AccessShareLock);
+												   AccessExclusiveLock);
 			newrte = addRangeTableEntryForRelation(sub_pstate, rel,
 												   makeAlias("new", NIL),
 												   false, false,
-												   AccessShareLock);
+												   AccessExclusiveLock);
 			oldrte->requiredPerms = 0;
 			newrte->requiredPerms = 0;
 			addRTEtoQuery(sub_pstate, oldrte, false, true, false);
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index b22d2d1457..2d8eae62e6 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -100,9 +100,8 @@ SetLocktagRelationOid(LOCKTAG *tag, Oid relid)
  *
  * Lock a relation given only its OID.  This should generally be used
  * before attempting to open the relation's relcache entry.
- * Return true if we acquired a new lock, false if already held.
  */
-bool
+void
 LockRelationOid(Oid relid, LOCKMODE lockmode)
 {
 	LOCKTAG		tag;
@@ -133,10 +132,22 @@ LockRelationOid(Oid relid, LOCKMODE lockmode)
 	{
 		AcceptInvalidationMessages();
 		MarkLockClear(locallock);
-		return true;
 	}
+}
+
+/*
+ *		CheckRelationLockedByUs
+ *
+ * Returns true we hold a lock on 'relid' with 'lockmode'.
+ */
+bool
+CheckRelationLockedByUs(Oid relid, LOCKMODE lockmode)
+{
+	LOCKTAG		tag;
+
+	SetLocktagRelationOid(&tag, relid);
 
-	return res == LOCKACQUIRE_ALREADY_HELD;
+	return LocalLockExists(&tag, lockmode);
 }
 
 /*
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 831ae62525..8c140fe4b0 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -563,6 +563,30 @@ DoLockModesConflict(LOCKMODE mode1, LOCKMODE mode2)
 	return false;
 }
 
+/*
+ * LocalLockExists -- check if a log with 'locktag' is held locally with
+ *		'lockmode'
+ */
+bool
+LocalLockExists(const LOCKTAG *locktag, LOCKMODE lockmode)
+{
+	LOCALLOCKTAG	localtag;
+	bool			found;
+
+	MemSet(&localtag, 0, sizeof(localtag)); /* must clear padding */
+	localtag.lock = *locktag;
+	localtag.mode = lockmode;
+
+	/*
+	 * Find the LOCALLOCK entry for this lock and lockmode
+	 */
+	(void) hash_search(LockMethodLocalHash,
+					   (void *) &localtag,
+					   HASH_FIND, &found);
+
+	return found;
+}
+
 /*
  * LockHasWaiters -- look up 'locktag' and check if releasing this
  *		lock would wake up other processes waiting for it.
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 93259a4ffa..b03d8c591f 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -580,8 +580,8 @@ ExecRangeTableRelation(EState *estate, Index rti)
 		RangeTblEntry *rte = exec_rt_fetch(rti, estate->es_range_table_array);
 
 		/*
-		 * No need to lock the relation lock, because upstream code
-		 * must hold the lock already.
+		 * No need to lock the relation.  Locks were already
+		 * obtained by the upstream code.
 		 */
 		rel = estate->es_relations[rti - 1] = heap_open(rte->relid, NoLock);
 	}
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index b2ab3fd2b6..639235d622 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -482,7 +482,8 @@ typedef struct EState
 	List	   *es_range_table;	/* List of RangeTblEntry */
 	struct RangeTblEntry **es_range_table_array;	/* 0-based range table */
 	int			es_range_table_size;	/* size of the range table array */
-	Relation   *es_relations;	/* 0-based array of Relation pointers */
+	Relation   *es_relations;	/* 0-based array of Relations
+								 * es_range_table_size elements in length. */
 	PlannedStmt *es_plannedstmt;	/* link to top of plan tree */
 	const char *es_sourceText;	/* Source text from QueryDesc */
 
@@ -590,7 +591,7 @@ typedef struct EState
  */
 #define exec_rt_fetch(rangetblidx, rangetbl) rangetbl[(rangetblidx) - 1]
 
-/* XXX moved from parsetree.h. okay??
+/*
  *		getrelid
  *
  *		Given the range index of a relation, return the corresponding
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2743e1ea81..d844e323f0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -977,8 +977,7 @@ typedef struct RangeTblEntry
 	 */
 	Oid			relid;			/* OID of the relation */
 	char		relkind;		/* relation kind (see pg_class.relkind) */
-	LOCKMODE	lockmode;		/* lock level to obtain on the relation, or
-								 * 0 if no lock required */
+	LOCKMODE	lockmode;		/* lock level to obtain on the relation */
 	struct TableSampleClause *tablesample;	/* sampling info, or NULL */
 
 	/*
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 59dd26b04e..452627dd03 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -212,8 +212,9 @@ typedef struct ModifyTable
 	Plan		plan;
 	CmdType		operation;		/* INSERT, UPDATE, or DELETE */
 	bool		canSetTag;		/* do we set the command tag/es_processed? */
-	Index		nominalRelation;	/* RT index of the target table specified
-									 * in the command */
+	Index		targetRelation;	/* RT index of the target table specified in
+								 * the command or first child for inheritance
+								 * tables */
 	bool		partitionedTarget;	/* is the target table partitioned? */
 	bool		partColsUpdated;	/* some part key in hierarchy updated */
 	List	   *resultRelations;	/* integer list of RT indexes */
@@ -1092,7 +1093,7 @@ typedef struct PartitionPruneInfo
 typedef struct PartitionedRelPruneInfo
 {
 	NodeTag		type;
-	Index		rtindex;		/* Table's RT index */
+	Index		rtindex;		/* Partition table's RT index */
 	List	   *pruning_steps;	/* List of PartitionPruneStep, see below */
 	Bitmapset  *present_parts;	/* Indexes of all partitions which subplans or
 								 * subparts are present for. */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index c9f288c24d..bcc2ba6db5 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1713,8 +1713,9 @@ typedef struct ModifyTablePath
 	Path		path;
 	CmdType		operation;		/* INSERT, UPDATE, or DELETE */
 	bool		canSetTag;		/* do we set the command tag/es_processed? */
-	Index		nominalRelation;	/* RT index of the target table specified
-									 * in the command */
+	Index		targetRelation;	/* RT index of the target table specified in
+								 * the command or first child for inheritance
+								 * tables */
 	bool		partitionedTarget;	/* is the target table partitioned? */
 	bool		partColsUpdated;	/* some part key in hierarchy updated */
 	List	   *resultRelations;	/* integer list of RT indexes */
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index d826e3cc55..a45c2407d0 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -238,7 +238,7 @@ extern LockRowsPath *create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
 extern ModifyTablePath *create_modifytable_path(PlannerInfo *root,
 						RelOptInfo *rel,
 						CmdType operation, bool canSetTag,
-						Index nominalRelation, bool partitionedTarget,
+						Index targetRelation, bool partitionedTarget,
 						bool partColsUpdated,
 						List *resultRelations, List *subpaths,
 						List *subroots,
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index 69e6f7ffd6..abfafe5e40 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -37,7 +37,8 @@ typedef enum XLTW_Oper
 extern void RelationInitLockInfo(Relation relation);
 
 /* Lock a relation */
-extern bool LockRelationOid(Oid relid, LOCKMODE lockmode);
+extern void LockRelationOid(Oid relid, LOCKMODE lockmode);
+extern bool CheckRelationLockedByUs(Oid relid, LOCKMODE lockmode);
 extern bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode);
 extern void UnlockRelationId(LockRelId *relid, LOCKMODE lockmode);
 extern void UnlockRelationOid(Oid relid, LOCKMODE lockmode);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index ff4df7fec5..b4a71ced0b 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -540,6 +540,7 @@ extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
 extern void LockReleaseSession(LOCKMETHODID lockmethodid);
 extern void LockReleaseCurrentOwner(LOCALLOCK **locallocks, int nlocks);
 extern void LockReassignCurrentOwner(LOCALLOCK **locallocks, int nlocks);
+extern bool LocalLockExists(const LOCKTAG *locktag, LOCKMODE lockmode);
 extern bool LockHasWaiters(const LOCKTAG *locktag,
 			   LOCKMODE lockmode, bool sessionLock);
 extern VirtualTransactionId *GetLockConflicts(const LOCKTAG *locktag,
