commit 7ae3b66331c268862ef9058fb7ca5419949efbd9
Author: Tom Lane <tgl@sss.pgh.pa.us>
Date:   Sun Jul 10 12:04:07 2022 -0400

    Improve performance of adjust_appendrel_attrs_multilevel.
    
    The present implementations of adjust_appendrel_attrs_multilevel and
    its sibling adjust_child_relids_multilevel are very messy, because
    they work by reconstructing the relids of the child's immediate
    parent and then seeing if that's bms_equal to the relids of the
    target parent.  Aside from being quite inefficient, this will not
    work for joinrels whose relids contain outer-join relids in addition
    to baserels.
    
    The whole thing can be solved at a stroke by adding explicit parent
    and top_parent links to child RelOptInfos, and making these functions
    work with RelOptInfo pointers instead of relids.  Doing that is
    simpler for most callers, too.
    
    In my original version of this patch, I got rid of
    RelOptInfo.top_parent_relids on the grounds that it was now redundant.
    However, that adds a lot of code churn in places that otherwise would
    not need changing, and arguably the extra indirection needed to fetch
    top_parent->relids in those places costs something.  So this version
    leaves that field in place.

diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 60c0e3f108..f8a97622b1 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1760,8 +1760,8 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
 	if (IS_OTHER_REL(inner_rel) && result != NIL)
 		result = (List *) adjust_appendrel_attrs_multilevel(root,
 															(Node *) result,
-															inner_rel->relids,
-															inner_rel->top_parent_relids);
+															inner_rel,
+															inner_rel->top_parent);
 
 	return result;
 }
@@ -2626,8 +2626,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 					child_expr = (Expr *)
 						adjust_appendrel_attrs_multilevel(root,
 														  (Node *) cur_em->em_expr,
-														  child_relids,
-														  top_parent_relids);
+														  child_rel,
+														  child_rel->top_parent);
 				}
 
 				/*
@@ -2768,8 +2768,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 					child_expr = (Expr *)
 						adjust_appendrel_attrs_multilevel(root,
 														  (Node *) cur_em->em_expr,
-														  child_relids,
-														  top_parent_relids);
+														  child_joinrel,
+														  child_joinrel->top_parent);
 				}
 
 				/*
@@ -2791,8 +2791,8 @@ add_child_join_rel_equivalences(PlannerInfo *root,
 					new_nullable_relids =
 						adjust_child_relids_multilevel(root,
 													   new_nullable_relids,
-													   child_relids,
-													   top_parent_relids);
+													   child_joinrel,
+													   child_joinrel->top_parent);
 
 				(void) add_eq_member(cur_ec, child_expr,
 									 new_relids, new_nullable_relids,
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 06ad856eac..f6baa2a765 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1791,8 +1791,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 							withCheckOptions = (List *)
 								adjust_appendrel_attrs_multilevel(root,
 																  (Node *) withCheckOptions,
-																  this_result_rel->relids,
-																  top_result_rel->relids);
+																  this_result_rel,
+																  top_result_rel);
 						withCheckOptionLists = lappend(withCheckOptionLists,
 													   withCheckOptions);
 					}
@@ -1804,8 +1804,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 							returningList = (List *)
 								adjust_appendrel_attrs_multilevel(root,
 																  (Node *) returningList,
-																  this_result_rel->relids,
-																  top_result_rel->relids);
+																  this_result_rel,
+																  top_result_rel);
 						returningLists = lappend(returningLists,
 												 returningList);
 					}
@@ -1826,13 +1826,13 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
 							leaf_action->qual =
 								adjust_appendrel_attrs_multilevel(root,
 																  (Node *) action->qual,
-																  this_result_rel->relids,
-																  top_result_rel->relids);
+																  this_result_rel,
+																  top_result_rel);
 							leaf_action->targetList = (List *)
 								adjust_appendrel_attrs_multilevel(root,
 																  (Node *) action->targetList,
-																  this_result_rel->relids,
-																  top_result_rel->relids);
+																  this_result_rel,
+																  top_result_rel);
 							if (leaf_action->commandType == CMD_UPDATE)
 								leaf_action->updateColnos =
 									adjust_inherited_attnums_multilevel(root,
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 9d4bb47027..62cccf9d87 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -479,39 +479,34 @@ adjust_appendrel_attrs_mutator(Node *node,
 
 /*
  * adjust_appendrel_attrs_multilevel
- *	  Apply Var translations from a toplevel appendrel parent down to a child.
+ *	  Apply Var translations from an appendrel parent down to a child.
  *
- * In some cases we need to translate expressions referencing a parent relation
- * to reference an appendrel child that's multiple levels removed from it.
+ * Replace Vars in the "node" expression that reference "parentrel" with
+ * the appropriate Vars for "childrel".  childrel can be more than one
+ * inheritance level removed from parentrel.
  */
 Node *
 adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
-								  Relids child_relids,
-								  Relids top_parent_relids)
+								  RelOptInfo *childrel,
+								  RelOptInfo *parentrel)
 {
 	AppendRelInfo **appinfos;
-	Bitmapset  *parent_relids = NULL;
 	int			nappinfos;
-	int			cnt;
-
-	Assert(bms_num_members(child_relids) == bms_num_members(top_parent_relids));
-
-	appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
 
-	/* Construct relids set for the immediate parent of given child. */
-	for (cnt = 0; cnt < nappinfos; cnt++)
+	/* Recurse if immediate parent is not the top parent. */
+	if (childrel->parent != parentrel)
 	{
-		AppendRelInfo *appinfo = appinfos[cnt];
-
-		parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
+		if (childrel->parent)
+			node = adjust_appendrel_attrs_multilevel(root, node,
+													 childrel->parent,
+													 parentrel);
+		else
+			elog(ERROR, "childrel is not a child of parentrel");
 	}
 
-	/* Recurse if immediate parent is not the top parent. */
-	if (!bms_equal(parent_relids, top_parent_relids))
-		node = adjust_appendrel_attrs_multilevel(root, node, parent_relids,
-												 top_parent_relids);
+	/* Now translate for this child. */
+	appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
 
-	/* Now translate for this child */
 	node = adjust_appendrel_attrs(root, node, nappinfos, appinfos);
 
 	pfree(appinfos);
@@ -554,56 +549,43 @@ adjust_child_relids(Relids relids, int nappinfos, AppendRelInfo **appinfos)
 }
 
 /*
- * Replace any relid present in top_parent_relids with its child in
- * child_relids. Members of child_relids can be multiple levels below top
- * parent in the partition hierarchy.
+ * Substitute child's relids for parent's relids in a Relid set.
+ * The childrel can be multiple inheritance levels below the parent.
  */
 Relids
 adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
-							   Relids child_relids, Relids top_parent_relids)
+							   RelOptInfo *childrel,
+							   RelOptInfo *parentrel)
 {
 	AppendRelInfo **appinfos;
 	int			nappinfos;
-	Relids		parent_relids = NULL;
-	Relids		result;
-	Relids		tmp_result = NULL;
-	int			cnt;
 
 	/*
-	 * If the given relids set doesn't contain any of the top parent relids,
-	 * it will remain unchanged.
+	 * If the given relids set doesn't contain any of the parent relids, it
+	 * will remain unchanged.
 	 */
-	if (!bms_overlap(relids, top_parent_relids))
+	if (!bms_overlap(relids, parentrel->relids))
 		return relids;
 
-	appinfos = find_appinfos_by_relids(root, child_relids, &nappinfos);
-
-	/* Construct relids set for the immediate parent of the given child. */
-	for (cnt = 0; cnt < nappinfos; cnt++)
-	{
-		AppendRelInfo *appinfo = appinfos[cnt];
-
-		parent_relids = bms_add_member(parent_relids, appinfo->parent_relid);
-	}
-
 	/* Recurse if immediate parent is not the top parent. */
-	if (!bms_equal(parent_relids, top_parent_relids))
+	if (childrel->parent != parentrel)
 	{
-		tmp_result = adjust_child_relids_multilevel(root, relids,
-													parent_relids,
-													top_parent_relids);
-		relids = tmp_result;
+		if (childrel->parent)
+			relids = adjust_child_relids_multilevel(root, relids,
+													childrel->parent,
+													parentrel);
+		else
+			elog(ERROR, "childrel is not a child of parentrel");
 	}
 
-	result = adjust_child_relids(relids, nappinfos, appinfos);
+	/* Now translate for this child. */
+	appinfos = find_appinfos_by_relids(root, childrel->relids, &nappinfos);
+
+	relids = adjust_child_relids(relids, nappinfos, appinfos);
 
-	/* Free memory consumed by any intermediate result. */
-	if (tmp_result)
-		bms_free(tmp_result);
-	bms_free(parent_relids);
 	pfree(appinfos);
 
-	return result;
+	return relids;
 }
 
 /*
@@ -694,8 +676,8 @@ get_translated_update_targetlist(PlannerInfo *root, Index relid,
 		*processed_tlist = (List *)
 			adjust_appendrel_attrs_multilevel(root,
 											  (Node *) root->processed_tlist,
-											  bms_make_singleton(relid),
-											  bms_make_singleton(root->parse->resultRelation));
+											  find_base_rel(root, relid),
+											  find_base_rel(root, root->parse->resultRelation));
 		if (update_colnos)
 			*update_colnos =
 				adjust_inherited_attnums_multilevel(root, root->update_colnos,
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 483c4f4137..833b440f39 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -3994,8 +3994,8 @@ reparameterize_path_by_child(PlannerInfo *root, Path *path,
 #define ADJUST_CHILD_ATTRS(node) \
 	((node) = \
 	 (List *) adjust_appendrel_attrs_multilevel(root, (Node *) (node), \
-												child_rel->relids, \
-												child_rel->top_parent_relids))
+												child_rel, \
+												child_rel->top_parent))
 
 #define REPARAMETERIZE_CHILD_PATH(path) \
 do { \
@@ -4212,8 +4212,8 @@ do { \
 	old_ppi = new_path->param_info;
 	required_outer =
 		adjust_child_relids_multilevel(root, old_ppi->ppi_req_outer,
-									   child_rel->relids,
-									   child_rel->top_parent_relids);
+									   child_rel,
+									   child_rel->top_parent);
 
 	/* If we already have a PPI for this parameterization, just return it */
 	new_ppi = find_param_path_info(new_path->parent, required_outer);
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 520409f4ba..a163853bed 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -265,14 +265,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	 */
 	if (parent)
 	{
-		/*
-		 * Each direct or indirect child wants to know the relids of its
-		 * topmost parent.
-		 */
-		if (parent->top_parent_relids)
-			rel->top_parent_relids = parent->top_parent_relids;
-		else
-			rel->top_parent_relids = bms_copy(parent->relids);
+		/* We keep back-links to immediate parent and topmost parent. */
+		rel->parent = parent;
+		rel->top_parent = parent->top_parent ? parent->top_parent : parent;
+		rel->top_parent_relids = rel->top_parent->relids;
 
 		/*
 		 * Also propagate lateral-reference information from appendrel parent
@@ -294,6 +290,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
 	}
 	else
 	{
+		rel->parent = NULL;
+		rel->top_parent = NULL;
 		rel->top_parent_relids = NULL;
 		rel->direct_lateral_relids = NULL;
 		rel->lateral_relids = NULL;
@@ -663,6 +661,8 @@ build_join_rel(PlannerInfo *root,
 	joinrel->joininfo = NIL;
 	joinrel->has_eclass_joins = false;
 	joinrel->consider_partitionwise_join = false;	/* might get changed later */
+	joinrel->parent = NULL;
+	joinrel->top_parent = NULL;
 	joinrel->top_parent_relids = NULL;
 	joinrel->part_scheme = NULL;
 	joinrel->nparts = -1;
@@ -842,7 +842,9 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->joininfo = NIL;
 	joinrel->has_eclass_joins = false;
 	joinrel->consider_partitionwise_join = false;	/* might get changed later */
-	joinrel->top_parent_relids = NULL;
+	joinrel->parent = parent_joinrel;
+	joinrel->top_parent = parent_joinrel->top_parent ? parent_joinrel->top_parent : parent_joinrel;
+	joinrel->top_parent_relids = joinrel->top_parent->relids;
 	joinrel->part_scheme = NULL;
 	joinrel->nparts = -1;
 	joinrel->boundinfo = NULL;
@@ -854,9 +856,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
 	joinrel->partexprs = NULL;
 	joinrel->nullable_partexprs = NULL;
 
-	joinrel->top_parent_relids = bms_union(outer_rel->top_parent_relids,
-										   inner_rel->top_parent_relids);
-
 	/* Compute information relevant to foreign relations. */
 	set_foreign_rel_properties(joinrel, outer_rel, inner_rel);
 
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 9d3c05aed3..aeabc2aca9 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -529,8 +529,8 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
 			partprunequal = (List *)
 				adjust_appendrel_attrs_multilevel(root,
 												  (Node *) prunequal,
-												  subpart->relids,
-												  targetpart->relids);
+												  subpart,
+												  targetpart);
 		}
 
 		/*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 44ffc73f15..ee8bed6452 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -915,7 +915,15 @@ typedef struct RelOptInfo
 	 */
 	/* consider partitionwise join paths? (if partitioned rel) */
 	bool		consider_partitionwise_join;
-	/* Relids of topmost parents (if "other" rel) */
+
+	/*
+	 * inheritance links, if this is an otherrel (otherwise NULL):
+	 */
+	/* Immediate parent relation (dumping it would be too verbose) */
+	struct RelOptInfo *parent pg_node_attr(read_write_ignore);
+	/* Topmost parent relation (dumping it would be too verbose) */
+	struct RelOptInfo *top_parent pg_node_attr(read_write_ignore);
+	/* Relids of topmost parent (redundant, but handy) */
 	Relids		top_parent_relids;
 
 	/*
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
index fc808dcd27..5e80a741a4 100644
--- a/src/include/optimizer/appendinfo.h
+++ b/src/include/optimizer/appendinfo.h
@@ -23,13 +23,13 @@ extern AppendRelInfo *make_append_rel_info(Relation parentrel,
 extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
 									int nappinfos, AppendRelInfo **appinfos);
 extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
-											   Relids child_relids,
-											   Relids top_parent_relids);
+											   RelOptInfo *childrel,
+											   RelOptInfo *parentrel);
 extern Relids adjust_child_relids(Relids relids, int nappinfos,
 								  AppendRelInfo **appinfos);
 extern Relids adjust_child_relids_multilevel(PlannerInfo *root, Relids relids,
-											 Relids child_relids,
-											 Relids top_parent_relids);
+											 RelOptInfo *childrel,
+											 RelOptInfo *parentrel);
 extern List *adjust_inherited_attnums(List *attnums, AppendRelInfo *context);
 extern List *adjust_inherited_attnums_multilevel(PlannerInfo *root,
 												 List *attnums,
