fix-inherited-column-aliases-a-1.patch
text/x-diff
Filename: fix-inherited-column-aliases-a-1.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| contrib/postgres_fdw/expected/postgres_fdw.out | 6 | 6 |
| src/backend/optimizer/util/appendinfo.c | 24 | 5 |
| src/backend/optimizer/util/inherit.c | 90 | 11 |
| src/include/optimizer/appendinfo.h | 2 | 1 |
| src/test/regress/expected/partition_prune.out | 21 | 0 |
| src/test/regress/sql/partition_prune.sql | 5 | 0 |
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 14180ee..5f7aa31 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -8507,7 +8507,7 @@ SELECT t1.a,t2.b,t2.c FROM fprt1 t1 LEFT JOIN (SELECT * FROM fprt2 WHERE a < 10)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.a, ftprt2_p1.b, ftprt2_p1.c
- Relations: (public.ftprt1_p1 t1) LEFT JOIN (public.ftprt2_p1 fprt2)
+ Relations: (public.ftprt1_p1 t1) LEFT JOIN (public.ftprt2_p1)
Remote SQL: SELECT r5.a, r6.b, r6.c FROM (public.fprt1_p1 r5 LEFT JOIN public.fprt2_p1 r6 ON (((r5.a = r6.b)) AND ((r5.b = r6.a)) AND ((r6.a < 10)))) WHERE ((r5.a < 10)) ORDER BY r5.a ASC NULLS LAST, r6.b ASC NULLS LAST, r6.c ASC NULLS LAST
(4 rows)
@@ -8689,17 +8689,17 @@ SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 O
SET enable_partitionwise_aggregate TO true;
EXPLAIN (COSTS OFF)
SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
- QUERY PLAN
-----------------------------------------------------------------------
+ QUERY PLAN
+-------------------------------------------------------------
Sort
Sort Key: fpagg_tab_p1.a
-> Append
-> Foreign Scan
- Relations: Aggregate on (public.fpagg_tab_p1 pagg_tab)
+ Relations: Aggregate on (public.fpagg_tab_p1)
-> Foreign Scan
- Relations: Aggregate on (public.fpagg_tab_p2 pagg_tab)
+ Relations: Aggregate on (public.fpagg_tab_p2)
-> Foreign Scan
- Relations: Aggregate on (public.fpagg_tab_p3 pagg_tab)
+ Relations: Aggregate on (public.fpagg_tab_p3)
(9 rows)
SELECT a, sum(b), min(b), count(*) FROM pagg_tab GROUP BY a HAVING avg(b) < 22 ORDER BY 1;
diff --git a/src/backend/optimizer/util/appendinfo.c b/src/backend/optimizer/util/appendinfo.c
index 16d3151..bc354fc 100644
--- a/src/backend/optimizer/util/appendinfo.c
+++ b/src/backend/optimizer/util/appendinfo.c
@@ -34,7 +34,8 @@ typedef struct
static void make_inh_translation_list(Relation oldrelation,
Relation newrelation,
Index newvarno,
- List **translated_vars);
+ List **translated_vars,
+ AttrNumber **parent_colnos);
static Node *adjust_appendrel_attrs_mutator(Node *node,
adjust_appendrel_attrs_context *context);
static List *adjust_inherited_tlist(List *tlist,
@@ -44,10 +45,13 @@ static List *adjust_inherited_tlist(List *tlist,
/*
* make_append_rel_info
* Build an AppendRelInfo for the parent-child pair
+ *
+ * Also make a reverse-translation array (XXX maybe add that to AppendRelInfo)
*/
AppendRelInfo *
make_append_rel_info(Relation parentrel, Relation childrel,
- Index parentRTindex, Index childRTindex)
+ Index parentRTindex, Index childRTindex,
+ AttrNumber **parent_colnos)
{
AppendRelInfo *appinfo = makeNode(AppendRelInfo);
@@ -56,7 +60,7 @@ make_append_rel_info(Relation parentrel, Relation childrel,
appinfo->parent_reltype = parentrel->rd_rel->reltype;
appinfo->child_reltype = childrel->rd_rel->reltype;
make_inh_translation_list(parentrel, childrel, childRTindex,
- &appinfo->translated_vars);
+ &appinfo->translated_vars, parent_colnos);
appinfo->parent_reloid = RelationGetRelid(parentrel);
return appinfo;
@@ -65,16 +69,24 @@ 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)
+ List **translated_vars,
+ AttrNumber **parent_colnos)
{
List *vars = NIL;
+ AttrNumber *pcolnos;
TupleDesc old_tupdesc = RelationGetDescr(oldrelation);
TupleDesc new_tupdesc = RelationGetDescr(newrelation);
Oid new_relid = RelationGetRelid(newrelation);
@@ -83,6 +95,10 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
int old_attno;
int new_attno = 0;
+ /* Initialize reverse-translation array with all entries zero */
+ *parent_colnos = pcolnos =
+ (AttrNumber *) palloc0(newnatts * sizeof(AttrNumber));
+
for (old_attno = 0; old_attno < oldnatts; old_attno++)
{
Form_pg_attribute att;
@@ -115,6 +131,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
atttypmod,
attcollation,
0));
+ pcolnos[old_attno] = old_attno + 1;
continue;
}
@@ -138,6 +155,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,6 +175,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
atttypmod,
attcollation,
0));
+ pcolnos[new_attno] = old_attno + 1;
new_attno++;
}
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 38bc61e..88c85ce 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -38,10 +38,12 @@
static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
RangeTblEntry *parentrte,
Index parentRTindex, Relation parentrel,
+ RangeTblEntry *top_parentrte,
PlanRowMark *top_parentrc, LOCKMODE lockmode);
static void expand_single_inheritance_child(PlannerInfo *root,
RangeTblEntry *parentrte,
Index parentRTindex, Relation parentrel,
+ RangeTblEntry *top_parentrte,
PlanRowMark *top_parentrc, Relation childrel,
RangeTblEntry **childrte_p,
Index *childRTindex_p);
@@ -141,7 +143,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
* extract the partition key columns of all the partitioned tables.
*/
expand_partitioned_rtentry(root, rel, rte, rti,
- oldrelation, oldrc, lockmode);
+ oldrelation, rte, oldrc, lockmode);
}
else
{
@@ -201,7 +203,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
/* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
expand_single_inheritance_child(root, rte, rti, oldrelation,
- oldrc, newrelation,
+ rte, oldrc, newrelation,
&childrte, &childRTindex);
/* Create the otherrel RelOptInfo too. */
@@ -284,6 +286,7 @@ static void
expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
RangeTblEntry *parentrte,
Index parentRTindex, Relation parentrel,
+ RangeTblEntry *top_parentrte,
PlanRowMark *top_parentrc, LOCKMODE lockmode)
{
PartitionDesc partdesc;
@@ -370,7 +373,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
/* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
expand_single_inheritance_child(root, parentrte, parentRTindex,
- parentrel, top_parentrc, childrel,
+ parentrel, top_parentrte,
+ top_parentrc, childrel,
&childrte, &childRTindex);
/* Create the otherrel RelOptInfo too. */
@@ -381,7 +385,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
expand_partitioned_rtentry(root, childrelinfo,
childrte, childRTindex,
- childrel, top_parentrc, lockmode);
+ childrel, top_parentrte,
+ top_parentrc, lockmode);
/* Close child relation, but keep locks */
table_close(childrel, NoLock);
@@ -403,7 +408,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
* allMarkTypes field still accumulates values from all descendents.
*
* "parentrte" and "parentRTindex" are immediate parent's RTE and
- * RTI. "top_parentrc" is top parent's PlanRowMark.
+ * RTI. "top_parentrte" is top parent's RTE, and "top_parentrc"
+ * is top parent's PlanRowMark.
*
* The child RangeTblEntry and its RTI are returned in "childrte_p" and
* "childRTindex_p" resp.
@@ -411,6 +417,7 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
static void
expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
Index parentRTindex, Relation parentrel,
+ RangeTblEntry *top_parentrte,
PlanRowMark *top_parentrc, Relation childrel,
RangeTblEntry **childrte_p,
Index *childRTindex_p)
@@ -421,10 +428,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
RangeTblEntry *childrte;
Index childRTindex;
AppendRelInfo *appinfo;
+ AttrNumber *parent_colnos;
+ TupleDesc child_tupdesc;
+ List *parent_colnames;
+ List *child_colnames;
+ const char *child_tablealias;
/*
* 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 +444,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,21 +464,77 @@ 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;
/*
* Build an AppendRelInfo struct for each parent/child pair.
*/
appinfo = make_append_rel_info(parentrel, childrel,
- parentRTindex, childRTindex);
+ parentRTindex, childRTindex,
+ &parent_colnos);
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 (parent_colnos[cattno] > 0 &&
+ parent_colnos[cattno] <= list_length(parent_colnames))
+ {
+ /* Duplicate the query-assigned name for the parent column */
+ attname = strVal(list_nth(parent_colnames,
+ parent_colnos[cattno] - 1));
+ }
+ else
+ {
+ /* New column, just use its real name */
+ attname = NameStr(att->attname);
+ }
+ child_colnames = lappend(child_colnames, makeString(pstrdup(attname)));
+ }
+
+ /*
+ * If the query specified a table alias for the original parent table,
+ * duplicate that for each child. (If the plan gets printed, ruleutils.c
+ * has to sort out unique aliases to use, which it can handle.) Otherwise,
+ * just use the child's real name.
+ */
+ if (top_parentrte->alias)
+ child_tablealias = top_parentrte->alias->aliasname;
+ else
+ child_tablealias = RelationGetRelationName(childrel);
+ childrte->alias = childrte->eref = makeAlias(child_tablealias,
+ 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 +551,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
diff --git a/src/include/optimizer/appendinfo.h b/src/include/optimizer/appendinfo.h
index 9fdb6a6..a2fa0d8 100644
--- a/src/include/optimizer/appendinfo.h
+++ b/src/include/optimizer/appendinfo.h
@@ -19,7 +19,8 @@
extern AppendRelInfo *make_append_rel_info(Relation parentrel,
Relation childrel,
- Index parentRTindex, Index childRTindex);
+ Index parentRTindex, Index childRTindex,
+ AttrNumber **parent_colnos);
extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
int nappinfos, AppendRelInfo **appinfos);
extern Node *adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 12c0109..f3d9f55 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -1245,6 +1245,12 @@ CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a);
CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2);
CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a);
CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT;
+CREATE TABLE part_rev (b INT, c INT, a INT);
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- fail
+ERROR: table "part_rev" contains column "c" not found in parent "part"
+DETAIL: The new partition may contain only the columns present in parent.
+ALTER TABLE part_rev DROP COLUMN c;
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- now it's ok
INSERT INTO part VALUES (-1,-1), (1,1), (2,NULL), (NULL,-2),(NULL,NULL);
EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS NULL ORDER BY 1, 2, 3;
QUERY PLAN
@@ -1255,6 +1261,21 @@ EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS
Filter: (a IS NULL)
(4 rows)
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM part p(x) ORDER BY x;
+ QUERY PLAN
+-----------------------------------------------
+ Sort
+ Output: p.x, p.b
+ Sort Key: p.x
+ -> Append
+ -> Seq Scan on public.part_p1 p
+ Output: p.x, p.b
+ -> Seq Scan on public.part_rev p_1
+ Output: p_1.x, p_1.b
+ -> Seq Scan on public.part_p2_p1 p_2
+ Output: p_2.x, p_2.b
+(10 rows)
+
--
-- some more cases
--
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index a5900e5..41f0b6f 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -202,8 +202,13 @@ CREATE TABLE part (a INT, b INT) PARTITION BY LIST (a);
CREATE TABLE part_p1 PARTITION OF part FOR VALUES IN (-2,-1,0,1,2);
CREATE TABLE part_p2 PARTITION OF part DEFAULT PARTITION BY RANGE(a);
CREATE TABLE part_p2_p1 PARTITION OF part_p2 DEFAULT;
+CREATE TABLE part_rev (b INT, c INT, a INT);
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- fail
+ALTER TABLE part_rev DROP COLUMN c;
+ALTER TABLE part ATTACH PARTITION part_rev FOR VALUES IN (3); -- now it's ok
INSERT INTO part VALUES (-1,-1), (1,1), (2,NULL), (NULL,-2),(NULL,NULL);
EXPLAIN (COSTS OFF) SELECT tableoid::regclass as part, a, b FROM part WHERE a IS NULL ORDER BY 1, 2, 3;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM part p(x) ORDER BY x;
--
-- some more cases