From 2f17b57017e5f1993dbe0f389e01f1302a541196 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 18 Aug 2022 12:36:06 -0400 Subject: [PATCH] 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 with planned future changes to make joinrels' relid sets 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. Discussion: https://postgr.es/m/553080.1657481916@sss.pgh.pa.us --- src/backend/optimizer/path/equivclass.c | 16 ++--- src/backend/optimizer/plan/planner.c | 16 ++--- src/backend/optimizer/util/appendinfo.c | 96 ++++++++++--------------- src/backend/optimizer/util/pathnode.c | 8 +-- src/backend/optimizer/util/relnode.c | 23 +++--- src/backend/partitioning/partprune.c | 4 +- src/include/nodes/pathnodes.h | 10 ++- src/include/optimizer/appendinfo.h | 8 +-- 8 files changed, 85 insertions(+), 96 deletions(-) diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 2c900e6b12..799bdc91d0 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 b781080dba..cf9e0a74db 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1793,8 +1793,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); } @@ -1806,8 +1806,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); } @@ -1828,13 +1828,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++) - { - 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)) - node = adjust_appendrel_attrs_multilevel(root, node, parent_relids, - top_parent_relids); + if (childrel->parent != parentrel) + { + if (childrel->parent) + node = adjust_appendrel_attrs_multilevel(root, node, + childrel->parent, + parentrel); + else + elog(ERROR, "childrel is not a child of parentrel"); + } + + /* 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 dd64b46086..e10561d843 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -4026,8 +4026,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 { \ @@ -4244,8 +4244,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 965eb5b3b4..edcdd0a360 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; @@ -843,7 +843,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; @@ -855,9 +857,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 fc19232c7b..bf9fe5b7aa 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 bdc7b50db9..294cfe9c47 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -938,7 +938,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,