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;
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;
FLATCOPY(newnode, appinfo, AppendRelInfo);
MUTATE(newnode->translated_vars, appinfo->translated_vars, List *);
+ /* Assume nothing need be done with parent_colnos[] */
return (Node *) newnode;
}
break;
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);
}
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,
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);
* 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);
continue;
vars = lappend(vars, makeVarFromTargetEntry(newvarno, tle));
+ pcolnos[tle->resno - 1] = tle->resno;
}
- *translated_vars = vars;
+ appinfo->translated_vars = vars;
}
/*
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,
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;
/*
* 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);
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;
atttypmod,
attcollation,
0));
+ pcolnos[old_attno] = old_attno + 1;
continue;
}
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);
atttypmod,
attcollation,
0));
+ pcolnos[new_attno] = old_attno + 1;
new_attno++;
}
- *translated_vars = vars;
+ appinfo->translated_vars = vars;
}
/*
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];
* "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.
- *
- * 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.
+ * 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, 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
*/
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