aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/inherit.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2019-12-02 19:08:10 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2019-12-02 19:08:10 -0500
commit55a1954da16e041f895e5c3a6abff13c5e3a4a2f (patch)
treea7259305e0cd81599279d92d2f41c05c89b8bd51 /src/backend/optimizer/util/inherit.c
parentce76c0ba53e4bd0daf3db7a703671b27797b7244 (diff)
downloadpostgresql-55a1954da16e041f895e5c3a6abff13c5e3a4a2f.tar.gz
postgresql-55a1954da16e041f895e5c3a6abff13c5e3a4a2f.zip
Fix EXPLAIN's column alias output for mismatched child tables.
If an inheritance/partitioning parent table is assigned some column alias names in the query, EXPLAIN mapped those aliases onto the child tables' columns by physical position, resulting in bogus output if a child table's columns aren't one-for-one with the parent's. To fix, make expand_single_inheritance_child() generate a correctly re-mapped column alias list, rather than just copying the parent RTE's alias node. (We have to fill the alias field, not just adjust the eref field, because ruleutils.c will ignore eref in favor of looking at the real column names.) This means that child tables will now always have alias fields in plan rtables, where before they might not have. That results in a rather substantial set of regression test output changes: EXPLAIN will now always show child tables with aliases that match the parent table (usually with "_N" appended for uniqueness). But that seems like a net positive for understandability, since the parent alias corresponds to something that actually appeared in the original query, while the child table names didn't. (Note that this does not change anything for cases where an explicit table alias was written in the query for the parent table; it just makes cases without such aliases behave similarly to that.) Hence, while we could avoid these subsidiary changes if we made inherit.c more complicated, we choose not to. Discussion: https://postgr.es/m/12424.1575168015@sss.pgh.pa.us
Diffstat (limited to 'src/backend/optimizer/util/inherit.c')
-rw-r--r--src/backend/optimizer/util/inherit.c74
1 files changed, 69 insertions, 5 deletions
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 38bc61e6878..a38df47e47e 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -421,10 +421,13 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
RangeTblEntry *childrte;
Index childRTindex;
AppendRelInfo *appinfo;
+ TupleDesc child_tupdesc;
+ List *parent_colnames;
+ List *child_colnames;
/*
* 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 +435,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,8 +455,11 @@ 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;
/*
@@ -459,10 +469,57 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
parentRTindex, childRTindex);
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 (appinfo->parent_colnos[cattno] > 0 &&
+ appinfo->parent_colnos[cattno] <= list_length(parent_colnames))
+ {
+ /* Duplicate the query-assigned name for the parent column */
+ attname = strVal(list_nth(parent_colnames,
+ appinfo->parent_colnos[cattno] - 1));
+ }
+ else
+ {
+ /* New column, just use its real name */
+ attname = NameStr(att->attname);
+ }
+ child_colnames = lappend(child_colnames, makeString(pstrdup(attname)));
+ }
+
+ /*
+ * We just duplicate the parent's table alias name for each child. If the
+ * plan gets printed, ruleutils.c has to sort out unique table aliases to
+ * use, which it can handle.
+ */
+ childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname,
+ 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 +536,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