diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 36bf1dc92bb..3b54e3d80ce 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2303,10 +2303,10 @@ _copyRangeTblEntry(const RangeTblEntry *from)
 	COPY_STRING_FIELD(ctename);
 	COPY_SCALAR_FIELD(ctelevelsup);
 	COPY_SCALAR_FIELD(self_reference);
-	COPY_STRING_FIELD(enrname);
 	COPY_NODE_FIELD(coltypes);
 	COPY_NODE_FIELD(coltypmods);
 	COPY_NODE_FIELD(colcollations);
+	COPY_STRING_FIELD(enrname);
 	COPY_NODE_FIELD(alias);
 	COPY_NODE_FIELD(eref);
 	COPY_SCALAR_FIELD(lateral);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 5bcf0317dc8..696cd6e7e1b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2631,6 +2631,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
 	COMPARE_NODE_FIELD(coltypes);
 	COMPARE_NODE_FIELD(coltypmods);
 	COMPARE_NODE_FIELD(colcollations);
+	COMPARE_STRING_FIELD(enrname);
 	COMPARE_NODE_FIELD(alias);
 	COMPARE_NODE_FIELD(eref);
 	COMPARE_SCALAR_FIELD(lateral);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index f062c6b9f1e..781cdf8ff99 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -4777,14 +4777,12 @@ set_namedtuplestore_size_estimates(PlannerInfo *root, RelOptInfo *rel)
 	Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
 
 	/*
-	 * Use the estimate provided by the code which is generating the named
-	 * tuplestore.  In some cases, the actual number might be available; in
-	 * others the same plan will be re-used, so a "typical" value might be
-	 * estimated and used.
+	 * XXX We could use the estimate provided by the code which is generating
+	 * the named tuplestore in EphemeralNamedRelationMetadataData member
+	 * 'enrtuples'.  We don't have access to QueryEnvironment to look it up
+	 * from here yet, so for now we'll use a bogus default estimate.
 	 */
-	rel->tuples = rte->enrtuples;
-	if (rel->tuples < 0)
-		rel->tuples = 1000;
+	rel->tuples = 1000;
 
 	/* Now estimate number of output rows, etc */
 	set_baserel_size_estimates(root, rel);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index e412d0f9d30..f99e547745c 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -2020,7 +2020,6 @@ addRangeTableEntryForENR(ParseState *pstate,
 	rte->eref = makeAlias(refname, NIL);
 	buildRelationAliases(tupdesc, alias, rte->eref);
 	rte->enrname = enrmd->name;
-	rte->enrtuples = enrmd->enrtuples;
 	rte->coltypes = NIL;
 	rte->coltypmods = NIL;
 	rte->colcollations = NIL;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2d2e2c0fbc1..8be5c3a9c09 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -944,6 +944,11 @@ typedef struct RangeTblEntry
 
 	/*
 	 * Fields valid for a plain relation RTE (else zero):
+	 *
+	 * As a special case, RTE_NAMEDTUPLESTORE can also set relid to indicate
+	 * that the tuple format of the tuplestore is the same as the referenced
+	 * relation.  This allows plans referencing AFTER trigger transition
+	 * tables to be invalidated if the underlying table is altered.
 	 */
 	Oid			relid;			/* OID of the relation */
 	char		relkind;		/* relation kind (see pg_class.relkind) */
@@ -1004,18 +1009,24 @@ typedef struct RangeTblEntry
 	bool		self_reference; /* is this a recursive self-reference? */
 
 	/*
-	 * Fields valid for values and CTE RTEs (else NIL):
+	 * Fields valid for table functions, values, CTE and ENR RTEs (else NIL):
 	 *
 	 * We need these for CTE RTEs so that the types of self-referential
 	 * columns are well-defined.  For VALUES RTEs, storing these explicitly
 	 * saves having to re-determine the info by scanning the values_lists.
+	 * For ENRs, we store the types explicitly here (we could get the
+	 * information from the catalogs if 'relid' was supplied, but we'd still
+	 * need these for TupleDesc-based ENRs, so we might as well always store
+	 * the type info here).
 	 */
 	List	   *coltypes;		/* OID list of column type OIDs */
 	List	   *coltypmods;		/* integer list of column typmods */
 	List	   *colcollations;	/* OID list of column collation OIDs */
 
+	/*
+	 * Fields valid for ENR RTEs (else NULL/zero):
+	 */
 	char	   *enrname;		/* name of ephemeral named relation */
-	double		enrtuples;		/* estimated or actual from caller */
 
 	/*
 	 * Fields valid in all RTEs:
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 7ebbde60d30..66a9c9fc0c8 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5757,13 +5757,17 @@ CREATE TRIGGER transition_table_base_upd_trig
 UPDATE transition_table_base
   SET val = '*' || val || '*'
   WHERE id BETWEEN 2 AND 3;
-INFO:  Hash Full Join
+INFO:  Merge Full Join
   Output: COALESCE(ot.id, nt.id), ot.val, nt.val
-  Hash Cond: (ot.id = nt.id)
-  ->  Named Tuplestore Scan
+  Merge Cond: (ot.id = nt.id)
+  ->  Sort
         Output: ot.id, ot.val
-  ->  Hash
+        Sort Key: ot.id
+        ->  Named Tuplestore Scan
+              Output: ot.id, ot.val
+  ->  Sort
         Output: nt.id, nt.val
+        Sort Key: nt.id
         ->  Named Tuplestore Scan
               Output: nt.id, nt.val
 
