From ce76c0ba53e4bd0daf3db7a703671b27797b7244 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 2 Dec 2019 18:05:29 -0500 Subject: [PATCH] Add a reverse-translation column number array to struct AppendRelInfo. This provides for cheaper mapping of child columns back to parent columns. The one existing use-case in examine_simple_variable() would hardly justify this by itself; but an upcoming bug fix will make use of this array in a mainstream code path, and it seems likely that we'll find other uses for it as we continue to build out the partitioning infrastructure. Discussion: https://postgr.es/m/12424.1575168015@sss.pgh.pa.us --- src/backend/nodes/copyfuncs.c | 2 ++ src/backend/nodes/equalfuncs.c | 2 ++ src/backend/nodes/nodeFuncs.c | 1 + src/backend/nodes/outfuncs.c | 2 ++ src/backend/optimizer/prep/prepjointree.c | 18 +++++++++++----- src/backend/optimizer/util/appendinfo.c | 26 +++++++++++++++++------ src/backend/utils/adt/selfuncs.c | 24 ++++++--------------- src/include/nodes/pathnodes.h | 25 +++++++++++++--------- 8 files changed, 61 insertions(+), 39 deletions(-) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 2f267e4bb6..a74b56bb59 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2327,6 +2327,8 @@ _copyAppendRelInfo(const AppendRelInfo *from) COPY_SCALAR_FIELD(parent_reltype); COPY_SCALAR_FIELD(child_reltype); COPY_NODE_FIELD(translated_vars); + COPY_SCALAR_FIELD(num_child_cols); + COPY_POINTER_FIELD(parent_colnos, from->num_child_cols * sizeof(AttrNumber)); COPY_SCALAR_FIELD(parent_reloid); return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index da0e1d139a..2fcd4a3467 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -900,6 +900,8 @@ _equalAppendRelInfo(const AppendRelInfo *a, const AppendRelInfo *b) COMPARE_SCALAR_FIELD(parent_reltype); COMPARE_SCALAR_FIELD(child_reltype); COMPARE_NODE_FIELD(translated_vars); + COMPARE_SCALAR_FIELD(num_child_cols); + COMPARE_POINTER_FIELD(parent_colnos, a->num_child_cols * sizeof(AttrNumber)); COMPARE_SCALAR_FIELD(parent_reloid); return true; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index bcada74042..880b0ec684 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -3103,6 +3103,7 @@ expression_tree_mutator(Node *node, FLATCOPY(newnode, appinfo, AppendRelInfo); MUTATE(newnode->translated_vars, appinfo->translated_vars, List *); + /* Assume nothing need be done with parent_colnos[] */ return (Node *) newnode; } break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b0dcd02ff6..a80eccc2c1 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2509,6 +2509,8 @@ _outAppendRelInfo(StringInfo str, const AppendRelInfo *node) WRITE_OID_FIELD(parent_reltype); WRITE_OID_FIELD(child_reltype); WRITE_NODE_FIELD(translated_vars); + WRITE_INT_FIELD(num_child_cols); + WRITE_ATTRNUMBER_ARRAY(parent_colnos, node->num_child_cols); WRITE_OID_FIELD(parent_reloid); } diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index f489f140e3..db25bcf441 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -81,7 +81,7 @@ static void pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, Query *setOpQuery, int childRToffset); static void make_setop_translation_list(Query *query, Index newvarno, - List **translated_vars); + AppendRelInfo *appinfo); static bool is_simple_subquery(Query *subquery, RangeTblEntry *rte, JoinExpr *lowest_outer_join); static Node *pull_up_simple_values(PlannerInfo *root, Node *jtnode, @@ -1313,8 +1313,7 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, appinfo->child_relid = childRTindex; appinfo->parent_reltype = InvalidOid; appinfo->child_reltype = InvalidOid; - make_setop_translation_list(setOpQuery, childRTindex, - &appinfo->translated_vars); + make_setop_translation_list(setOpQuery, childRTindex, appinfo); appinfo->parent_reloid = InvalidOid; root->append_rel_list = lappend(root->append_rel_list, appinfo); @@ -1356,14 +1355,22 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, * a UNION ALL member. (At this point it's just a simple list of * referencing Vars, but if we succeed in pulling up the member * subquery, the Vars will get replaced by pulled-up expressions.) + * Also create the rather trivial reverse-translation array. */ static void make_setop_translation_list(Query *query, Index newvarno, - List **translated_vars) + AppendRelInfo *appinfo) { List *vars = NIL; + AttrNumber *pcolnos; ListCell *l; + /* Initialize reverse-translation array with all entries zero */ + /* (entries for resjunk columns will stay that way) */ + appinfo->num_child_cols = list_length(query->targetList); + appinfo->parent_colnos = pcolnos = + (AttrNumber *) palloc0(appinfo->num_child_cols * sizeof(AttrNumber)); + foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); @@ -1372,9 +1379,10 @@ make_setop_translation_list(Query *query, Index newvarno, continue; vars = lappend(vars, makeVarFromTargetEntry(newvarno, tle)); + pcolnos[tle->resno - 1] = tle->resno; } - *translated_vars = vars; + appinfo->translated_vars = vars; } /* diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c index 16d315176e..1890f256de 100644 --- a/src/backend/optimizer/util/appendinfo.c +++ b/src/backend/optimizer/util/appendinfo.c @@ -34,7 +34,7 @@ typedef struct static void make_inh_translation_list(Relation oldrelation, Relation newrelation, Index newvarno, - List **translated_vars); + AppendRelInfo *appinfo); static Node *adjust_appendrel_attrs_mutator(Node *node, adjust_appendrel_attrs_context *context); static List *adjust_inherited_tlist(List *tlist, @@ -55,8 +55,7 @@ make_append_rel_info(Relation parentrel, Relation childrel, appinfo->child_relid = childRTindex; appinfo->parent_reltype = parentrel->rd_rel->reltype; appinfo->child_reltype = childrel->rd_rel->reltype; - make_inh_translation_list(parentrel, childrel, childRTindex, - &appinfo->translated_vars); + make_inh_translation_list(parentrel, childrel, childRTindex, appinfo); appinfo->parent_reloid = RelationGetRelid(parentrel); return appinfo; @@ -65,16 +64,23 @@ 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) + AppendRelInfo *appinfo) { List *vars = NIL; + AttrNumber *pcolnos; TupleDesc old_tupdesc = RelationGetDescr(oldrelation); TupleDesc new_tupdesc = RelationGetDescr(newrelation); Oid new_relid = RelationGetRelid(newrelation); @@ -83,6 +89,11 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, int old_attno; int new_attno = 0; + /* Initialize reverse-translation array with all entries zero */ + appinfo->num_child_cols = newnatts; + appinfo->parent_colnos = pcolnos = + (AttrNumber *) palloc0(newnatts * sizeof(AttrNumber)); + for (old_attno = 0; old_attno < oldnatts; old_attno++) { Form_pg_attribute att; @@ -115,6 +126,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, atttypmod, attcollation, 0)); + pcolnos[old_attno] = old_attno + 1; continue; } @@ -138,6 +150,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,10 +170,11 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, atttypmod, attcollation, 0)); + pcolnos[new_attno] = old_attno + 1; new_attno++; } - *translated_vars = vars; + appinfo->translated_vars = vars; } /* diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 35dbd728ec..ff02b5aafa 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -4769,29 +4769,17 @@ examine_simple_variable(PlannerInfo *root, Var *var, root)->rtekind == RTE_RELATION) { int parent_varattno; - ListCell *l; - parent_varattno = 1; found = false; - foreach(l, appinfo->translated_vars) - { - Var *childvar = lfirst_node(Var, l); - - /* Ignore dropped attributes of the parent. */ - if (childvar != NULL && - varattno == childvar->varattno) - { - found = true; - break; - } - parent_varattno++; - } - - if (!found) - break; + if (varattno <= 0 || varattno > appinfo->num_child_cols) + break; /* safety check */ + parent_varattno = appinfo->parent_colnos[varattno - 1]; + if (parent_varattno == 0) + break; /* Var is local to child */ varno = appinfo->parent_relid; varattno = parent_varattno; + found = true; /* If the parent is itself a child, continue up. */ appinfo = root->append_rel_array[varno]; diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 23a06d718e..6dab810d68 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -2151,17 +2151,13 @@ struct SpecialJoinInfo * "append relation" (essentially, a list of child RTEs), we build an * AppendRelInfo for each child RTE. The list of AppendRelInfos indicates * which child RTEs must be included when expanding the parent, and each node - * carries information needed to translate Vars referencing the parent into - * Vars referencing that child. + * carries information needed to translate between columns of the parent and + * columns of the child. * - * These structs are kept in the PlannerInfo node's append_rel_list. - * Note that we just throw all the structs into one list, and scan the - * whole list when desiring to expand any one parent. We could have used - * a more complex data structure (eg, one list per parent), but this would - * be harder to update during operations such as pulling up subqueries, - * and not really any easier to scan. Considering that typical queries - * will not have many different append parents, it doesn't seem worthwhile - * to complicate things. + * These structs are kept in the PlannerInfo node's append_rel_list, with + * append_rel_array[] providing a convenient lookup method for the struct + * associated with a particular child relid (there can be only one, though + * parent rels may have many entries in append_rel_list). * * Note: after completion of the planner prep phase, any given RTE is an * append parent having entries in append_rel_list if and only if its @@ -2218,6 +2214,15 @@ typedef struct AppendRelInfo */ List *translated_vars; /* Expressions in the child's Vars */ + /* + * This array simplifies translations in the reverse direction, from + * child's column numbers to parent's. The entry at [ccolno - 1] is the + * 1-based parent column number for child column ccolno, or zero if that + * child column is dropped or doesn't exist in the parent. + */ + int num_child_cols; /* length of array */ + AttrNumber *parent_colnos; /* array of parent attnos, or zeroes */ + /* * We store the parent table's OID here for inheritance, or InvalidOid for * UNION ALL. This is only needed to help in generating error messages if