aboutsummaryrefslogtreecommitdiff
path: root/contrib/postgres_fdw/postgres_fdw.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2018-01-12 16:52:49 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2018-01-12 16:52:49 -0500
commite9f2703ab7b29f7e9100807cfbd19ddebbaa0b12 (patch)
treec4c157a9abe4527596a9aff4b3aff988b23b2a91 /contrib/postgres_fdw/postgres_fdw.c
parent680d540502609b422d378a1b8e0c10cac3c60084 (diff)
downloadpostgresql-e9f2703ab7b29f7e9100807cfbd19ddebbaa0b12.tar.gz
postgresql-e9f2703ab7b29f7e9100807cfbd19ddebbaa0b12.zip
Fix postgres_fdw to cope with duplicate GROUP BY entries.
Commit 7012b132d, which added the ability to push down aggregates and grouping to the remote server, wasn't careful to ensure that the remote server would have the same idea we do about which columns are the grouping columns, in cases where there are textually identical GROUP BY expressions. Such cases typically led to "targetlist item has multiple sortgroupref labels" errors. To fix this reliably, switch over to using "GROUP BY column-number" syntax rather than "GROUP BY expression" in transmitted queries, and adjust foreign_grouping_ok() to be more careful about duplicating the sortgroupref labeling of the local pathtarget. Per bug #14890 from Sean Johnston. Back-patch to v10 where the buggy code was introduced. Jeevan Chalke, reviewed by Ashutosh Bapat Discussion: https://postgr.es/m/20171107134948.1508.94783@wrigleys.postgresql.org
Diffstat (limited to 'contrib/postgres_fdw/postgres_fdw.c')
-rw-r--r--contrib/postgres_fdw/postgres_fdw.c83
1 files changed, 39 insertions, 44 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index c6e1211f8f6..dc302ca7890 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -4591,7 +4591,7 @@ static bool
foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
{
Query *query = root->parse;
- PathTarget *grouping_target;
+ PathTarget *grouping_target = root->upper_targets[UPPERREL_GROUP_AGG];
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private;
PgFdwRelationInfo *ofpinfo;
List *aggvars;
@@ -4599,7 +4599,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
int i;
List *tlist = NIL;
- /* Grouping Sets are not pushable */
+ /* We currently don't support pushing Grouping Sets. */
if (query->groupingSets)
return false;
@@ -4607,7 +4607,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private;
/*
- * If underneath input relation has any local conditions, those conditions
+ * If underlying scan relation has any local conditions, those conditions
* are required to be applied before performing aggregation. Hence the
* aggregate cannot be pushed down.
*/
@@ -4615,21 +4615,11 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
return false;
/*
- * The targetlist expected from this node and the targetlist pushed down
- * to the foreign server may be different. The latter requires
- * sortgrouprefs to be set to push down GROUP BY clause, but should not
- * have those arising from ORDER BY clause. These sortgrouprefs may be
- * different from those in the plan's targetlist. Use a copy of path
- * target to record the new sortgrouprefs.
- */
- grouping_target = copy_pathtarget(root->upper_targets[UPPERREL_GROUP_AGG]);
-
- /*
- * Evaluate grouping targets and check whether they are safe to push down
- * to the foreign side. All GROUP BY expressions will be part of the
- * grouping target and thus there is no need to evaluate it separately.
- * While doing so, add required expressions into target list which can
- * then be used to pass to foreign server.
+ * Examine grouping expressions, as well as other expressions we'd need to
+ * compute, and check whether they are safe to push down to the foreign
+ * server. All GROUP BY expressions will be part of the grouping target
+ * and thus there is no need to search for them separately. Add grouping
+ * expressions into target list which will be passed to foreign server.
*/
i = 0;
foreach(lc, grouping_target->exprs)
@@ -4641,51 +4631,59 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
/* Check whether this expression is part of GROUP BY clause */
if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
{
+ TargetEntry *tle;
+
/*
- * If any of the GROUP BY expression is not shippable we can not
+ * If any GROUP BY expression is not shippable, then we cannot
* push down aggregation to the foreign server.
*/
if (!is_foreign_expr(root, grouped_rel, expr))
return false;
- /* Pushable, add to tlist */
- tlist = add_to_flat_tlist(tlist, list_make1(expr));
+ /*
+ * Pushable, so add to tlist. We need to create a TLE for this
+ * expression and apply the sortgroupref to it. We cannot use
+ * add_to_flat_tlist() here because that avoids making duplicate
+ * entries in the tlist. If there are duplicate entries with
+ * distinct sortgrouprefs, we have to duplicate that situation in
+ * the output tlist.
+ */
+ tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
+ tle->ressortgroupref = sgref;
+ tlist = lappend(tlist, tle);
}
else
{
- /* Check entire expression whether it is pushable or not */
+ /*
+ * Non-grouping expression we need to compute. Is it shippable?
+ */
if (is_foreign_expr(root, grouped_rel, expr))
{
- /* Pushable, add to tlist */
+ /* Yes, so add to tlist as-is; OK to suppress duplicates */
tlist = add_to_flat_tlist(tlist, list_make1(expr));
}
else
{
- /*
- * If we have sortgroupref set, then it means that we have an
- * ORDER BY entry pointing to this expression. Since we are
- * not pushing ORDER BY with GROUP BY, clear it.
- */
- if (sgref)
- grouping_target->sortgrouprefs[i] = 0;
-
- /* Not matched exactly, pull the var with aggregates then */
+ /* Not pushable as a whole; extract its Vars and aggregates */
aggvars = pull_var_clause((Node *) expr,
PVC_INCLUDE_AGGREGATES);
+ /*
+ * If any aggregate expression is not shippable, then we
+ * cannot push down aggregation to the foreign server.
+ */
if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars))
return false;
/*
- * Add aggregates, if any, into the targetlist. Plain var
- * nodes should be either same as some GROUP BY expression or
- * part of some GROUP BY expression. In later case, the query
- * cannot refer plain var nodes without the surrounding
- * expression. In both the cases, they are already part of
+ * Add aggregates, if any, into the targetlist. Plain Vars
+ * outside an aggregate can be ignored, because they should be
+ * either same as some GROUP BY column or part of some GROUP
+ * BY expression. In either case, they are already part of
* the targetlist and thus no need to add them again. In fact
- * adding pulled plain var nodes in SELECT clause will cause
- * an error on the foreign server if they are not same as some
- * GROUP BY expression.
+ * including plain Vars in the tlist when they do not match a
+ * GROUP BY column would cause the foreign server to complain
+ * that the shipped query is invalid.
*/
foreach(l, aggvars)
{
@@ -4701,7 +4699,7 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
}
/*
- * Classify the pushable and non-pushable having clauses and save them in
+ * Classify the pushable and non-pushable HAVING clauses and save them in
* remote_conds and local_conds of the grouped rel's fpinfo.
*/
if (root->hasHavingQual && query->havingQual)
@@ -4771,9 +4769,6 @@ foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel)
}
}
- /* Transfer any sortgroupref data to the replacement tlist */
- apply_pathtarget_labeling_to_tlist(tlist, grouping_target);
-
/* Store generated targetlist */
fpinfo->grouped_tlist = tlist;