diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 14180ee..5f7aa31 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -8507,7 +8507,7 @@ SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10)
 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Foreign Scan
    Output: t1.a, ftprt2_p1.b, ftprt2_p1.c
-   Relations: (public.ftprt1_p1 t1) LEFT JOIN (public.ftprt2_p1 fprt2)
+   Relations: (public.ftprt1_p1 t1) LEFT JOIN (public.ftprt2_p1)
    Remote SQL: SELECT r5.a, r6.b, r6.c FROM (public.fprt1_p1 r5 LEFT JOIN public.fprt2_p1 r6 ON (((r5.a = r6.b)) AND ((r5.b = r6.a)) AND ((r6.a < 10)))) WHERE ((r5.a < 10)) ORDER BY r5.a ASC NULLS LAST, r6.b ASC NULLS LAST, r6.c ASC NULLS LAST
 (4 rows)
 
@@ -8689,17 +8689,17 @@ SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 O
 SET enable_partitionwise_aggregate TO true;
 EXPLAIN (COSTS OFF)
 SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
-                              QUERY PLAN                              
-----------------------------------------------------------------------
+                         QUERY PLAN                          
+-------------------------------------------------------------
  Sort
    Sort Key: fpagg_tab_p1.a
    ->  Append
          ->  Foreign Scan
-               Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+               Relations: Aggregate on (public.fpagg_tab_p1)
          ->  Foreign Scan
-               Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab)
+               Relations: Aggregate on (public.fpagg_tab_p2)
          ->  Foreign Scan
-               Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab)
+               Relations: Aggregate on (public.fpagg_tab_p3)
 (9 rows)
 
 SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 16d3151..bc354fc 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -34,7 +34,8 @@ typedef struct
 static void make_inh_translation_list(Relation oldrelation,
 									  Relation newrelation,
 									  Index newvarno,
-									  List **translated_vars);
+									  List **translated_vars,
+									  AttrNumber **parent_colnos);
 static Node *adjust_appendrel_attrs_mutator(Node *node,
 											adjust_appendrel_attrs_context *context);
 static List *adjust_inherited_tlist(List *tlist,
@@ -44,10 +45,13 @@ static List *adjust_inherited_tlist(List *tlist,
 /*
  * make_append_rel_info
  *	  Build an AppendRelInfo for the parent-child pair
+ *
+ * Also make a reverse-translation array (XXX maybe add that to AppendRelInfo)
  */
 AppendRelInfo *
 make_append_rel_info(Relation parentrel, Relation childrel,
-					 Index parentRTindex, Index childRTindex)
+					 Index parentRTindex, Index childRTindex,
+					 AttrNumber **parent_colnos)
 {
 	AppendRelInfo *appinfo = makeNode(AppendRelInfo);
 
@@ -56,7 +60,7 @@ make_append_rel_info(Relation parentrel, Relation childrel,
 	appinfo->parent_reltype = parentrel->rd_rel->reltype;
 	appinfo->child_reltype = childrel->rd_rel->reltype;
 	make_inh_translation_list(parentrel, childrel, childRTindex,
-							  &appinfo->translated_vars);
+							  &appinfo->translated_vars, parent_colnos);
 	appinfo->parent_reloid = RelationGetRelid(parentrel);
 
 	return appinfo;
@@ -65,16 +69,24 @@ make_append_rel_info(Relation parentrel, Relation childrel,
 /*
  * make_inh_translation_list
  *	  Build the list of translations from parent Vars to child Vars for
- *	  an inheritance child.
+ *	  an inheritance child, as well as a reverse-translation array.
+ *
+ * The reverse-translation array has an entry for each child relation
+ * column, which is either the 1-based index of the corresponding parent
+ * column, or 0 if there's no match (that happens for dropped child columns,
+ * as well as child columns beyond those of the parent, which are allowed in
+ * traditional inheritance though not partitioning).
  *
  * For paranoia's sake, we match type/collation as well as attribute name.
  */
 static void
 make_inh_translation_list(Relation oldrelation, Relation newrelation,
 						  Index newvarno,
-						  List **translated_vars)
+						  List **translated_vars,
+						  AttrNumber **parent_colnos)
 {
 	List	   *vars = NIL;
+	AttrNumber *pcolnos;
 	TupleDesc	old_tupdesc = RelationGetDescr(oldrelation);
 	TupleDesc	new_tupdesc = RelationGetDescr(newrelation);
 	Oid			new_relid = RelationGetRelid(newrelation);
@@ -83,6 +95,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 	int			old_attno;
 	int			new_attno = 0;
 
+	/* Initialize reverse-translation array with all entries zero */
+	*parent_colnos = pcolnos =
+		(AttrNumber *) palloc0(newnatts * sizeof(AttrNumber));
+
 	for (old_attno = 0; old_attno < oldnatts; old_attno++)
 	{
 		Form_pg_attribute att;
@@ -115,6 +131,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 										 atttypmod,
 										 attcollation,
 										 0));
+			pcolnos[old_attno] = old_attno + 1;
 			continue;
 		}
 
@@ -138,6 +155,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 				elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
 					 attname, RelationGetRelationName(newrelation));
 			new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1;
+			Assert(new_attno >= 0 && new_attno < newnatts);
 			ReleaseSysCache(newtup);
 
 			att = TupleDescAttr(new_tupdesc, new_attno);
@@ -157,6 +175,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
 									 atttypmod,
 									 attcollation,
 									 0));
+		pcolnos[new_attno] = old_attno + 1;
 		new_attno++;
 	}
 
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 38bc61e..88c85ce 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -38,10 +38,12 @@
 static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 									   RangeTblEntry *parentrte,
 									   Index parentRTindex, Relation parentrel,
+									   RangeTblEntry *top_parentrte,
 									   PlanRowMark *top_parentrc, LOCKMODE lockmode);
 static void expand_single_inheritance_child(PlannerInfo *root,
 											RangeTblEntry *parentrte,
 											Index parentRTindex, Relation parentrel,
+											RangeTblEntry *top_parentrte,
 											PlanRowMark *top_parentrc, Relation childrel,
 											RangeTblEntry **childrte_p,
 											Index *childRTindex_p);
@@ -141,7 +143,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
 		 * extract the partition key columns of all the partitioned tables.
 		 */
 		expand_partitioned_rtentry(root, rel, rte, rti,
-								   oldrelation, oldrc, lockmode);
+								   oldrelation, rte, oldrc, lockmode);
 	}
 	else
 	{
@@ -201,7 +203,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
 
 			/* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
 			expand_single_inheritance_child(root, rte, rti, oldrelation,
-											oldrc, newrelation,
+											rte, oldrc, newrelation,
 											&childrte, &childRTindex);
 
 			/* Create the otherrel RelOptInfo too. */
@@ -284,6 +286,7 @@ static void
 expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 						   RangeTblEntry *parentrte,
 						   Index parentRTindex, Relation parentrel,
+						   RangeTblEntry *top_parentrte,
 						   PlanRowMark *top_parentrc, LOCKMODE lockmode)
 {
 	PartitionDesc partdesc;
@@ -370,7 +373,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 
 		/* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
 		expand_single_inheritance_child(root, parentrte, parentRTindex,
-										parentrel, top_parentrc, childrel,
+										parentrel, top_parentrte,
+										top_parentrc, childrel,
 										&childrte, &childRTindex);
 
 		/* Create the otherrel RelOptInfo too. */
@@ -381,7 +385,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 		if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 			expand_partitioned_rtentry(root, childrelinfo,
 									   childrte, childRTindex,
-									   childrel, top_parentrc, lockmode);
+									   childrel, top_parentrte,
+									   top_parentrc, lockmode);
 
 		/* Close child relation, but keep locks */
 		table_close(childrel, NoLock);
@@ -403,7 +408,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
  * allMarkTypes field still accumulates values from all descendents.
  *
  * "parentrte" and "parentRTindex" are immediate parent's RTE and
- * RTI. "top_parentrc" is top parent's PlanRowMark.
+ * RTI.  "top_parentrte" is top parent's RTE, and "top_parentrc"
+ * is top parent's PlanRowMark.
  *
  * The child RangeTblEntry and its RTI are returned in "childrte_p" and
  * "childRTindex_p" resp.
@@ -411,6 +417,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 static void
 expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 								Index parentRTindex, Relation parentrel,
+								RangeTblEntry *top_parentrte,
 								PlanRowMark *top_parentrc, Relation childrel,
 								RangeTblEntry **childrte_p,
 								Index *childRTindex_p)
@@ -421,10 +428,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	RangeTblEntry *childrte;
 	Index		childRTindex;
 	AppendRelInfo *appinfo;
+	AttrNumber *parent_colnos;
+	TupleDesc	child_tupdesc;
+	List	   *parent_colnames;
+	List	   *child_colnames;
+	const char *child_tablealias;
 
 	/*
 	 * Build an RTE for the child, and attach to query's rangetable list. We
-	 * copy most fields of the parent's RTE, but replace relation OID,
+	 * copy most scalar fields of the parent's RTE, but replace relation OID,
 	 * relkind, and inh for the child.  Also, set requiredPerms to zero since
 	 * all required permissions checks are done on the original RTE. Likewise,
 	 * set the child's securityQuals to empty, because we only want to apply
@@ -432,10 +444,14 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 	 * individual children may have.  (This is an intentional choice to make
 	 * inherited RLS work like regular permissions checks.) The parent
 	 * securityQuals will be propagated to children along with other base
-	 * restriction clauses, so we don't need to do it here.
+	 * restriction clauses, so we don't need to do it here.  Other
+	 * infrastructure of the parent RTE has to be translated to match the
+	 * child table's column ordering, which we do below, so a "flat" copy is
+	 * sufficient to start with.
 	 */
-	childrte = copyObject(parentrte);
-	*childrte_p = childrte;
+	childrte = makeNode(RangeTblEntry);
+	memcpy(childrte, parentrte, sizeof(RangeTblEntry));
+	Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
 	childrte->relid = childOID;
 	childrte->relkind = childrel->rd_rel->relkind;
 	/* A partitioned child will need to be expanded further. */
@@ -448,21 +464,77 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		childrte->inh = false;
 	childrte->requiredPerms = 0;
 	childrte->securityQuals = NIL;
+
+	/* Link not-yet-fully-filled child RTE into data structures */
 	parse->rtable = lappend(parse->rtable, childrte);
 	childRTindex = list_length(parse->rtable);
+	*childrte_p = childrte;
 	*childRTindex_p = childRTindex;
 
 	/*
 	 * Build an AppendRelInfo struct for each parent/child pair.
 	 */
 	appinfo = make_append_rel_info(parentrel, childrel,
-								   parentRTindex, childRTindex);
+								   parentRTindex, childRTindex,
+								   &parent_colnos);
 	root->append_rel_list = lappend(root->append_rel_list, appinfo);
 
+	/* tablesample is probably null, but copy it */
+	childrte->tablesample = copyObject(parentrte->tablesample);
+
+	/*
+	 * Construct an alias clause for the child, which we can also use as eref.
+	 * This is important so that EXPLAIN will print the right column aliases
+	 * for child-table columns.  (Since ruleutils.c doesn't have any easy way
+	 * to reassociate parent and child columns, we must get the child column
+	 * aliases right to start with.  Note that setting childrte->alias forces
+	 * ruleutils.c to use these column names, which it otherwise would not.)
+	 */
+	child_tupdesc = RelationGetDescr(childrel);
+	parent_colnames = parentrte->eref->colnames;
+	child_colnames = NIL;
+	for (int cattno = 0; cattno < child_tupdesc->natts; cattno++)
+	{
+		Form_pg_attribute att = TupleDescAttr(child_tupdesc, cattno);
+		const char *attname;
+
+		if (att->attisdropped)
+		{
+			/* Always insert an empty string for a dropped column */
+			attname = "";
+		}
+		else if (parent_colnos[cattno] > 0 &&
+				 parent_colnos[cattno] <= list_length(parent_colnames))
+		{
+			/* Duplicate the query-assigned name for the parent column */
+			attname = strVal(list_nth(parent_colnames,
+									  parent_colnos[cattno] - 1));
+		}
+		else
+		{
+			/* New column, just use its real name */
+			attname = NameStr(att->attname);
+		}
+		child_colnames = lappend(child_colnames, makeString(pstrdup(attname)));
+	}
+
+	/*
+	 * If the query specified a table alias for the original parent table,
+	 * duplicate that for each child.  (If the plan gets printed, ruleutils.c
+	 * has to sort out unique aliases to use, which it can handle.) Otherwise,
+	 * just use the child's real name.
+	 */
+	if (top_parentrte->alias)
+		child_tablealias = top_parentrte->alias->aliasname;
+	else
+		child_tablealias = RelationGetRelationName(childrel);
+	childrte->alias = childrte->eref = makeAlias(child_tablealias,
+												 child_colnames);
+
 	/*
 	 * Translate the column permissions bitmaps to the child's attnums (we
 	 * have to build the translated_vars list before we can do this).  But if
-	 * this is the parent table, we can leave copyObject's result alone.
+	 * this is the parent table, we can just duplicate the parent's bitmaps.
 	 *
 	 * Note: we need to do this even though the executor won't run any
 	 * permissions checks on the child RTE.  The insertedCols/updatedCols
@@ -479,6 +551,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
 		childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols,
 														 appinfo->translated_vars);
 	}
+	else
+	{
+		childrte->selectedCols = bms_copy(parentrte->selectedCols);
+		childrte->insertedCols = bms_copy(parentrte->insertedCols);
+		childrte->updatedCols = bms_copy(parentrte->updatedCols);
+		childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols);
+	}
 
 	/*
 	 * Store the RTE and appinfo in the respective PlannerInfo arrays, which
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
index 9fdb6a6..a2fa0d8 100644
--- a/src/include/optimizer/appendinfo.h
+++ b/src/include/optimizer/appendinfo.h
@@ -19,7 +19,8 @@
 
 extern AppendRelInfo *make_append_rel_info(Relation parentrel,
 										   Relation childrel,
-										   Index parentRTindex, Index childRTindex);
+										   Index parentRTindex, Index childRTindex,
+										   AttrNumber **parent_colnos);
 extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
 									int nappinfos, AppendRelInfo **appinfos);
 extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 12c0109..f3d9f55 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -1245,6 +1245,12 @@ CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a);
 CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2);
 CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a);
 CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT;
+CREATE TABLE part_rev (b INT, c INT, a INT);
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3);  -- fail
+ERROR:  table "part_rev" contains column "c" not found in parent "part"
+DETAIL:  The new partition may contain only the columns present in parent.
+ALTER TABLE part_rev DROP COLUMN c;
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3);  -- now it's ok
 INSERT INTO part VALUES (-1,-1), (1,1), (2,NULL), (NULL,-2),(NULL,NULL);
 EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS NULL ORDER BY 1, 2, 3;
                                 QUERY PLAN                                 
@@ -1255,6 +1261,21 @@ EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS
          Filter: (a IS NULL)
 (4 rows)
 
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM part p(x) ORDER BY x;
+                  QUERY PLAN                   
+-----------------------------------------------
+ Sort
+   Output: p.x, p.b
+   Sort Key: p.x
+   ->  Append
+         ->  Seq Scan on public.part_p1 p
+               Output: p.x, p.b
+         ->  Seq Scan on public.part_rev p_1
+               Output: p_1.x, p_1.b
+         ->  Seq Scan on public.part_p2_p1 p_2
+               Output: p_2.x, p_2.b
+(10 rows)
+
 --
 -- some more cases
 --
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index a5900e5..41f0b6f 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -202,8 +202,13 @@ CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a);
 CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2);
 CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a);
 CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT;
+CREATE TABLE part_rev (b INT, c INT, a INT);
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3);  -- fail
+ALTER TABLE part_rev DROP COLUMN c;
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3);  -- now it's ok
 INSERT INTO part VALUES (-1,-1), (1,1), (2,NULL), (NULL,-2),(NULL,NULL);
 EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS NULL ORDER BY 1, 2, 3;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM part p(x) ORDER BY x;
 
 --
 -- some more cases
