aboutsummaryrefslogtreecommitdiff
path: root/contrib/postgres_fdw/postgres_fdw.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/postgres_fdw/postgres_fdw.c')
-rw-r--r--contrib/postgres_fdw/postgres_fdw.c103
1 files changed, 75 insertions, 28 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 74dcaf65477..3dc717c4737 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -18,6 +18,7 @@
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/pg_class.h"
+#include "catalog/pg_opfamily.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/vacuum.h"
@@ -866,8 +867,6 @@ get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
foreach(lc, root->query_pathkeys)
{
PathKey *pathkey = (PathKey *) lfirst(lc);
- EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
- Expr *em_expr;
/*
* The planner and executor don't have any clever strategy for
@@ -875,13 +874,8 @@ get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
* getting it to be sorted by all of those pathkeys. We'll just
* end up resorting the entire data set. So, unless we can push
* down all of the query pathkeys, forget it.
- *
- * is_foreign_expr would detect volatile expressions as well, but
- * checking ec_has_volatile here saves some cycles.
*/
- if (pathkey_ec->ec_has_volatile ||
- !(em_expr = find_em_expr_for_rel(pathkey_ec, rel)) ||
- !is_foreign_expr(root, rel, em_expr))
+ if (!is_foreign_pathkey(root, rel, pathkey))
{
query_pathkeys_ok = false;
break;
@@ -928,16 +922,19 @@ get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
foreach(lc, useful_eclass_list)
{
EquivalenceClass *cur_ec = lfirst(lc);
- Expr *em_expr;
PathKey *pathkey;
/* If redundant with what we did above, skip it. */
if (cur_ec == query_ec)
continue;
+ /* Can't push down the sort if the EC's opfamily is not shippable. */
+ if (!is_shippable(linitial_oid(cur_ec->ec_opfamilies),
+ OperatorFamilyRelationId, fpinfo))
+ continue;
+
/* If no pushable expression for this rel, skip it. */
- em_expr = find_em_expr_for_rel(cur_ec, rel);
- if (em_expr == NULL || !is_foreign_expr(root, rel, em_expr))
+ if (find_em_for_rel(root, cur_ec, rel) == NULL)
continue;
/* Looks like we can generate a pathkey, so let's do it. */
@@ -6053,7 +6050,6 @@ add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
{
PathKey *pathkey = (PathKey *) lfirst(lc);
EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
- Expr *sort_expr;
/*
* is_foreign_expr would detect volatile expressions as well, but
@@ -6062,13 +6058,20 @@ add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
if (pathkey_ec->ec_has_volatile)
return;
- /* Get the sort expression for the pathkey_ec */
- sort_expr = find_em_expr_for_input_target(root,
- pathkey_ec,
- input_rel->reltarget);
+ /*
+ * Can't push down the sort if pathkey's opfamily is not shippable.
+ */
+ if (!is_shippable(pathkey->pk_opfamily, OperatorFamilyRelationId,
+ fpinfo))
+ return;
- /* If it's unsafe to remote, we cannot push down the final sort */
- if (!is_foreign_expr(root, input_rel, sort_expr))
+ /*
+ * The EC must contain a shippable EM that is computed in input_rel's
+ * reltarget, else we can't push down the sort.
+ */
+ if (find_em_for_rel_target(root,
+ pathkey_ec,
+ input_rel) == NULL)
return;
}
@@ -6602,14 +6605,55 @@ conversion_error_callback(void *arg)
}
/*
- * Find an equivalence class member expression to be computed as a sort column
- * in the given target.
+ * Given an EquivalenceClass and a foreign relation, find an EC member
+ * that can be used to sort the relation remotely according to a pathkey
+ * using this EC.
+ *
+ * If there is more than one suitable candidate, return an arbitrary
+ * one of them. If there is none, return NULL.
+ *
+ * This checks that the EC member expression uses only Vars from the given
+ * rel and is shippable. Caller must separately verify that the pathkey's
+ * ordering operator is shippable.
+ */
+EquivalenceMember *
+find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
+{
+ ListCell *lc;
+
+ foreach(lc, ec->ec_members)
+ {
+ EquivalenceMember *em = (EquivalenceMember *) lfirst(lc);
+
+ /*
+ * Note we require !bms_is_empty, else we'd accept constant
+ * expressions which are not suitable for the purpose.
+ */
+ if (bms_is_subset(em->em_relids, rel->relids) &&
+ !bms_is_empty(em->em_relids) &&
+ is_foreign_expr(root, rel, em->em_expr))
+ return em;
+ }
+
+ return NULL;
+}
+
+/*
+ * Find an EquivalenceClass member that is to be computed as a sort column
+ * in the given rel's reltarget, and is shippable.
+ *
+ * If there is more than one suitable candidate, return an arbitrary
+ * one of them. If there is none, return NULL.
+ *
+ * This checks that the EC member expression uses only Vars from the given
+ * rel and is shippable. Caller must separately verify that the pathkey's
+ * ordering operator is shippable.
*/
-Expr *
-find_em_expr_for_input_target(PlannerInfo *root,
- EquivalenceClass *ec,
- PathTarget *target)
+EquivalenceMember *
+find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
+ RelOptInfo *rel)
{
+ PathTarget *target = rel->reltarget;
ListCell *lc1;
int i;
@@ -6652,13 +6696,16 @@ find_em_expr_for_input_target(PlannerInfo *root,
while (em_expr && IsA(em_expr, RelabelType))
em_expr = ((RelabelType *) em_expr)->arg;
- if (equal(em_expr, expr))
- return em->em_expr;
+ if (!equal(em_expr, expr))
+ continue;
+
+ /* Check that expression (including relabels!) is shippable */
+ if (is_foreign_expr(root, rel, em->em_expr))
+ return em;
}
i++;
}
- elog(ERROR, "could not find pathkey item to sort");
- return NULL; /* keep compiler quiet */
+ return NULL;
}