]> granicus.if.org Git - postgresql/blobdiff - src/backend/optimizer/path/equivclass.c
Update copyright for 2014
[postgresql] / src / backend / optimizer / path / equivclass.c
index 5cdfec542cde91ded3271a62b9da53b63cabc20c..35d2a83599d7dfdaa0ecdcbc0d6cfab268dd27df 100644 (file)
@@ -6,7 +6,7 @@
  * See src/backend/optimizer/README for discussion of EquivalenceClasses.
  *
  *
- * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
@@ -21,6 +21,7 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
+#include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "optimizer/prep.h"
@@ -29,7 +30,7 @@
 
 
 static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
-                         Expr *expr, Relids relids,
+                         Expr *expr, Relids relids, Relids nullable_relids,
                          bool is_child, Oid datatype);
 static void generate_base_implied_equalities_const(PlannerInfo *root,
                                                                           EquivalenceClass *ec);
@@ -39,14 +40,15 @@ static void generate_base_implied_equalities_broken(PlannerInfo *root,
                                                                                EquivalenceClass *ec);
 static List *generate_join_implied_equalities_normal(PlannerInfo *root,
                                                                                EquivalenceClass *ec,
-                                                                               RelOptInfo *joinrel,
-                                                                               RelOptInfo *outer_rel,
-                                                                               RelOptInfo *inner_rel);
+                                                                               Relids join_relids,
+                                                                               Relids outer_relids,
+                                                                               Relids inner_relids);
 static List *generate_join_implied_equalities_broken(PlannerInfo *root,
                                                                                EquivalenceClass *ec,
-                                                                               RelOptInfo *joinrel,
-                                                                               RelOptInfo *outer_rel,
-                                                                               RelOptInfo *inner_rel);
+                                                                               Relids nominal_join_relids,
+                                                                               Relids outer_relids,
+                                                                               Relids nominal_inner_relids,
+                                                                               AppendRelInfo *inner_appinfo);
 static Oid select_equality_operator(EquivalenceClass *ec,
                                                 Oid lefttype, Oid righttype);
 static RestrictInfo *create_join_clause(PlannerInfo *root,
@@ -59,7 +61,6 @@ static bool reconsider_outer_join_clause(PlannerInfo *root,
                                                         bool outer_on_left);
 static bool reconsider_full_join_clause(PlannerInfo *root,
                                                        RestrictInfo *rinfo);
-static Index get_parent_relid(PlannerInfo *root, RelOptInfo *rel);
 
 
 /*
@@ -105,7 +106,9 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
        Expr       *item1;
        Expr       *item2;
        Relids          item1_relids,
-                               item2_relids;
+                               item2_relids,
+                               item1_nullable_relids,
+                               item2_nullable_relids;
        List       *opfamilies;
        EquivalenceClass *ec1,
                           *ec2;
@@ -162,6 +165,12 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
                        return false;           /* RHS is non-strict but not constant */
        }
 
+       /* Calculate nullable-relid sets for each side of the clause */
+       item1_nullable_relids = bms_intersect(item1_relids,
+                                                                                 restrictinfo->nullable_relids);
+       item2_nullable_relids = bms_intersect(item2_relids,
+                                                                                 restrictinfo->nullable_relids);
+
        /*
         * We use the declared input types of the operator, not exprType() of the
         * inputs, as the nominal datatypes for opfamily lookup.  This presumes
@@ -276,11 +285,19 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
                }
 
                /*
-                * Case 2: need to merge ec1 and ec2.  We add ec2's items to ec1, then
-                * set ec2's ec_merged link to point to ec1 and remove ec2 from the
-                * eq_classes list.  We cannot simply delete ec2 because that could
-                * leave dangling pointers in existing PathKeys.  We leave it behind
-                * with a link so that the merged EC can be found.
+                * Case 2: need to merge ec1 and ec2.  This should never happen after
+                * we've built any canonical pathkeys; if it did, those pathkeys might
+                * be rendered non-canonical by the merge.
+                */
+               if (root->canon_pathkeys != NIL)
+                       elog(ERROR, "too late to merge equivalence classes");
+
+               /*
+                * We add ec2's items to ec1, then set ec2's ec_merged link to point
+                * to ec1 and remove ec2 from the eq_classes list.      We cannot simply
+                * delete ec2 because that could leave dangling pointers in existing
+                * PathKeys.  We leave it behind with a link so that the merged EC can
+                * be found.
                 */
                ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
                ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
@@ -308,7 +325,8 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
        else if (ec1)
        {
                /* Case 3: add item2 to ec1 */
-               em2 = add_eq_member(ec1, item2, item2_relids, false, item2_type);
+               em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
+                                                       false, item2_type);
                ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
                ec1->ec_below_outer_join |= below_outer_join;
                /* mark the RI as associated with this eclass */
@@ -321,7 +339,8 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
        else if (ec2)
        {
                /* Case 3: add item1 to ec2 */
-               em1 = add_eq_member(ec2, item1, item1_relids, false, item1_type);
+               em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
+                                                       false, item1_type);
                ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
                ec2->ec_below_outer_join |= below_outer_join;
                /* mark the RI as associated with this eclass */
@@ -348,8 +367,10 @@ process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
                ec->ec_broken = false;
                ec->ec_sortref = 0;
                ec->ec_merged = NULL;
-               em1 = add_eq_member(ec, item1, item1_relids, false, item1_type);
-               em2 = add_eq_member(ec, item2, item2_relids, false, item2_type);
+               em1 = add_eq_member(ec, item1, item1_relids, item1_nullable_relids,
+                                                       false, item1_type);
+               em2 = add_eq_member(ec, item2, item2_relids, item2_nullable_relids,
+                                                       false, item2_type);
 
                root->eq_classes = lappend(root->eq_classes, ec);
 
@@ -430,13 +451,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
                                                                                        req_type,
                                                                                        -1,
                                                                                        req_collation,
-                                                                                       COERCE_DONTCARE);
+                                                                                       COERCE_IMPLICIT_CAST);
                else if (exprCollation((Node *) expr) != req_collation)
                        expr = (Expr *) makeRelabelType(expr,
                                                                                        req_type,
                                                                                        exprTypmod((Node *) expr),
                                                                                        req_collation,
-                                                                                       COERCE_DONTCARE);
+                                                                                       COERCE_IMPLICIT_CAST);
        }
 
        return expr;
@@ -447,12 +468,13 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation)
  */
 static EquivalenceMember *
 add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
-                         bool is_child, Oid datatype)
+                         Relids nullable_relids, bool is_child, Oid datatype)
 {
        EquivalenceMember *em = makeNode(EquivalenceMember);
 
        em->em_expr = expr;
        em->em_relids = relids;
+       em->em_nullable_relids = nullable_relids;
        em->em_is_const = false;
        em->em_is_child = is_child;
        em->em_datatype = datatype;
@@ -488,17 +510,24 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
  *       equivalence class it is a member of; if none, optionally build a new
  *       single-member EquivalenceClass for it.
  *
+ * expr is the expression, and nullable_relids is the set of base relids
+ * that are potentially nullable below it.     We actually only care about
+ * the set of such relids that are used in the expression; but for caller
+ * convenience, we perform that intersection step here.  The caller need
+ * only be sure that nullable_relids doesn't omit any nullable rels that
+ * might appear in the expr.
+ *
  * sortref is the SortGroupRef of the originating SortGroupClause, if any,
  * or zero if not.     (It should never be zero if the expression is volatile!)
  *
  * If rel is not NULL, it identifies a specific relation we're considering
  * a path for, and indicates that child EC members for that relation can be
- * considered.  Otherwise child members are ignored.  (Note: since child EC
+ * considered. Otherwise child members are ignored.  (Note: since child EC
  * members aren't guaranteed unique, a non-NULL value means that there could
  * be more than one EC that matches the expression; if so it's order-dependent
  * which one you get.  This is annoying but it only happens in corner cases,
- * so for now we live with just reporting the first match.  See also
- * generate_implied_equalities_for_indexcol and match_pathkeys_to_index.)
+ * so for now we live with just reporting the first match.     See also
+ * generate_implied_equalities_for_column and match_pathkeys_to_index.)
  *
  * If create_it is TRUE, we'll build a new EquivalenceClass when there is no
  * match.  If create_it is FALSE, we just return NULL when no match.
@@ -516,6 +545,7 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
 EquivalenceClass *
 get_eclass_for_sort_expr(PlannerInfo *root,
                                                 Expr *expr,
+                                                Relids nullable_relids,
                                                 List *opfamilies,
                                                 Oid opcintype,
                                                 Oid collation,
@@ -523,6 +553,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
                                                 Relids rel,
                                                 bool create_it)
 {
+       Relids          expr_relids;
        EquivalenceClass *newec;
        EquivalenceMember *newem;
        ListCell   *lc1;
@@ -533,6 +564,12 @@ get_eclass_for_sort_expr(PlannerInfo *root,
         */
        expr = canonicalize_ec_expression(expr, opcintype, collation);
 
+       /*
+        * Get the precise set of nullable relids appearing in the expression.
+        */
+       expr_relids = pull_varnos((Node *) expr);
+       nullable_relids = bms_intersect(nullable_relids, expr_relids);
+
        /*
         * Scan through the existing EquivalenceClasses for a match
         */
@@ -607,8 +644,8 @@ get_eclass_for_sort_expr(PlannerInfo *root,
        if (newec->ec_has_volatile && sortref == 0) /* should not happen */
                elog(ERROR, "volatile EquivalenceClass has no sortref");
 
-       newem = add_eq_member(newec, copyObject(expr), pull_varnos((Node *) expr),
-                                                 false, opcintype);
+       newem = add_eq_member(newec, copyObject(expr), expr_relids,
+                                                 nullable_relids, false, opcintype);
 
        /*
         * add_eq_member doesn't check for volatile functions, set-returning
@@ -755,7 +792,12 @@ generate_base_implied_equalities_const(PlannerInfo *root,
                }
        }
 
-       /* Find the constant member to use */
+       /*
+        * Find the constant member to use.  We prefer an actual constant to
+        * pseudo-constants (such as Params), because the constraint exclusion
+        * machinery might be able to exclude relations on the basis of generated
+        * "var = const" equalities, but "var = param" won't work for that.
+        */
        foreach(lc, ec->ec_members)
        {
                EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
@@ -763,7 +805,8 @@ generate_base_implied_equalities_const(PlannerInfo *root,
                if (cur_em->em_is_const)
                {
                        const_em = cur_em;
-                       break;
+                       if (IsA(cur_em->em_expr, Const))
+                               break;
                }
        }
        Assert(const_em != NULL);
@@ -788,7 +831,9 @@ generate_base_implied_equalities_const(PlannerInfo *root,
                }
                process_implied_equality(root, eq_op, ec->ec_collation,
                                                                 cur_em->em_expr, const_em->em_expr,
-                                                                ec->ec_relids,
+                                                                bms_copy(ec->ec_relids),
+                                                                bms_union(cur_em->em_nullable_relids,
+                                                                                  const_em->em_nullable_relids),
                                                                 ec->ec_below_outer_join,
                                                                 cur_em->em_is_const);
        }
@@ -843,7 +888,9 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
                        }
                        process_implied_equality(root, eq_op, ec->ec_collation,
                                                                         prev_em->em_expr, cur_em->em_expr,
-                                                                        ec->ec_relids,
+                                                                        bms_copy(ec->ec_relids),
+                                                                        bms_union(prev_em->em_nullable_relids,
+                                                                                          cur_em->em_nullable_relids),
                                                                         ec->ec_below_outer_join,
                                                                         false);
                }
@@ -879,7 +926,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
  * of the EC back into the main restrictinfo datastructures.  Multi-relation
  * clauses will be regurgitated later by generate_join_implied_equalities().
  * (We do it this way to maintain continuity with the case that ec_broken
- * becomes set only after we've gone up a join level or two.)
+ * becomes set only after we've gone up a join level or two.)  However, for
+ * an EC that contains constants, we can adopt a simpler strategy and just
+ * throw back all the source RestrictInfos immediately; that works because
+ * we know that such an EC can't become broken later.  (This rule justifies
+ * ignoring ec_has_const ECs in generate_join_implied_equalities, even when
+ * they are broken.)
  */
 static void
 generate_base_implied_equalities_broken(PlannerInfo *root,
@@ -891,7 +943,8 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
        {
                RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
 
-               if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
+               if (ec->ec_has_const ||
+                       bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
                        distribute_restrictinfo_to_rels(root, restrictinfo);
        }
 }
@@ -905,14 +958,26 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * that all equivalence-class members computable at that node are equal.
  * Since the set of clauses to enforce can vary depending on which subset
  * relations are the inputs, we have to compute this afresh for each join
- * path pair.  Hence a fresh List of RestrictInfo nodes is built and passed
- * back on each call.
+ * relation pair.  Hence a fresh List of RestrictInfo nodes is built and
+ * passed back on each call.
+ *
+ * In addition to its use at join nodes, this can be applied to generate
+ * eclass-based join clauses for use in a parameterized scan of a base rel.
+ * The reason for the asymmetry of specifying the inner rel as a RelOptInfo
+ * and the outer rel by Relids is that this usage occurs before we have
+ * built any join RelOptInfos.
+ *
+ * An annoying special case for parameterized scans is that the inner rel can
+ * be an appendrel child (an "other rel").     In this case we must generate
+ * appropriate clauses using child EC members. add_child_rel_equivalences
+ * must already have been done for the child rel.
  *
  * The results are sufficient for use in merge, hash, and plain nestloop join
  * methods.  We do not worry here about selecting clauses that are optimal
- * for use in a nestloop-with-parameterized-inner-scan.  indxpath.c makes
- * its own selections of clauses to use, and if the ones we pick here are
- * redundant with those, the extras will be eliminated in createplan.c.
+ * for use in a parameterized indexscan.  indxpath.c makes its own selections
+ * of clauses to use, and if the ones we pick here are redundant with those,
+ * the extras will be eliminated at createplan time, using the parent_ec
+ * markers that we provide (see is_redundant_derived_clause()).
  *
  * Because the same join clauses are likely to be needed multiple times as
  * we consider different join paths, we avoid generating multiple copies:
@@ -920,16 +985,41 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
  * we check to see if the pair matches any original clause (in ec_sources)
  * or previously-built clause (in ec_derives). This saves memory and allows
  * re-use of information cached in RestrictInfos.
+ *
+ * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
+ * We could simplify this function's API by computing it internally, but in
+ * all current uses, the caller has the value at hand anyway.
  */
 List *
 generate_join_implied_equalities(PlannerInfo *root,
-                                                                RelOptInfo *joinrel,
-                                                                RelOptInfo *outer_rel,
+                                                                Relids join_relids,
+                                                                Relids outer_relids,
                                                                 RelOptInfo *inner_rel)
 {
        List       *result = NIL;
+       Relids          inner_relids = inner_rel->relids;
+       Relids          nominal_inner_relids;
+       Relids          nominal_join_relids;
+       AppendRelInfo *inner_appinfo;
        ListCell   *lc;
 
+       /* If inner rel is a child, extra setup work is needed */
+       if (inner_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
+       {
+               /* Lookup parent->child translation data */
+               inner_appinfo = find_childrel_appendrelinfo(root, inner_rel);
+               /* Construct relids for the parent rel */
+               nominal_inner_relids = bms_make_singleton(inner_appinfo->parent_relid);
+               /* ECs will be marked with the parent's relid, not the child's */
+               nominal_join_relids = bms_union(outer_relids, nominal_inner_relids);
+       }
+       else
+       {
+               inner_appinfo = NULL;
+               nominal_inner_relids = inner_relids;
+               nominal_join_relids = join_relids;
+       }
+
        foreach(lc, root->eq_classes)
        {
                EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
@@ -944,23 +1034,24 @@ generate_join_implied_equalities(PlannerInfo *root,
                        continue;
 
                /* We can quickly ignore any that don't overlap the join, too */
-               if (!bms_overlap(ec->ec_relids, joinrel->relids))
+               if (!bms_overlap(ec->ec_relids, nominal_join_relids))
                        continue;
 
                if (!ec->ec_broken)
                        sublist = generate_join_implied_equalities_normal(root,
                                                                                                                          ec,
-                                                                                                                         joinrel,
-                                                                                                                         outer_rel,
-                                                                                                                         inner_rel);
+                                                                                                                         join_relids,
+                                                                                                                         outer_relids,
+                                                                                                                         inner_relids);
 
                /* Recover if we failed to generate required derived clauses */
                if (ec->ec_broken)
                        sublist = generate_join_implied_equalities_broken(root,
                                                                                                                          ec,
-                                                                                                                         joinrel,
-                                                                                                                         outer_rel,
-                                                                                                                         inner_rel);
+                                                                                                                nominal_join_relids,
+                                                                                                                         outer_relids,
+                                                                                                               nominal_inner_relids,
+                                                                                                                         inner_appinfo);
 
                result = list_concat(result, sublist);
        }
@@ -974,9 +1065,9 @@ generate_join_implied_equalities(PlannerInfo *root,
 static List *
 generate_join_implied_equalities_normal(PlannerInfo *root,
                                                                                EquivalenceClass *ec,
-                                                                               RelOptInfo *joinrel,
-                                                                               RelOptInfo *outer_rel,
-                                                                               RelOptInfo *inner_rel)
+                                                                               Relids join_relids,
+                                                                               Relids outer_relids,
+                                                                               Relids inner_relids)
 {
        List       *result = NIL;
        List       *new_members = NIL;
@@ -997,14 +1088,17 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
        {
                EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
 
-               if (cur_em->em_is_child)
-                       continue;                       /* ignore children here */
-               if (!bms_is_subset(cur_em->em_relids, joinrel->relids))
-                       continue;                       /* ignore --- not computable yet */
+               /*
+                * We don't need to check explicitly for child EC members.  This test
+                * against join_relids will cause them to be ignored except when
+                * considering a child inner rel, which is what we want.
+                */
+               if (!bms_is_subset(cur_em->em_relids, join_relids))
+                       continue;                       /* not computable yet, or wrong child */
 
-               if (bms_is_subset(cur_em->em_relids, outer_rel->relids))
+               if (bms_is_subset(cur_em->em_relids, outer_relids))
                        outer_members = lappend(outer_members, cur_em);
-               else if (bms_is_subset(cur_em->em_relids, inner_rel->relids))
+               else if (bms_is_subset(cur_em->em_relids, inner_relids))
                        inner_members = lappend(inner_members, cur_em);
                else
                        new_members = lappend(new_members, cur_em);
@@ -1140,13 +1234,17 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
  * generate_join_implied_equalities cleanup after failure
  *
  * Return any original RestrictInfos that are enforceable at this join.
+ *
+ * In the case of a child inner relation, we have to translate the
+ * original RestrictInfos from parent to child Vars.
  */
 static List *
 generate_join_implied_equalities_broken(PlannerInfo *root,
                                                                                EquivalenceClass *ec,
-                                                                               RelOptInfo *joinrel,
-                                                                               RelOptInfo *outer_rel,
-                                                                               RelOptInfo *inner_rel)
+                                                                               Relids nominal_join_relids,
+                                                                               Relids outer_relids,
+                                                                               Relids nominal_inner_relids,
+                                                                               AppendRelInfo *inner_appinfo)
 {
        List       *result = NIL;
        ListCell   *lc;
@@ -1154,13 +1252,25 @@ generate_join_implied_equalities_broken(PlannerInfo *root,
        foreach(lc, ec->ec_sources)
        {
                RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+               Relids          clause_relids = restrictinfo->required_relids;
 
-               if (bms_is_subset(restrictinfo->required_relids, joinrel->relids) &&
-                 !bms_is_subset(restrictinfo->required_relids, outer_rel->relids) &&
-                       !bms_is_subset(restrictinfo->required_relids, inner_rel->relids))
+               if (bms_is_subset(clause_relids, nominal_join_relids) &&
+                       !bms_is_subset(clause_relids, outer_relids) &&
+                       !bms_is_subset(clause_relids, nominal_inner_relids))
                        result = lappend(result, restrictinfo);
        }
 
+       /*
+        * If we have to translate, just brute-force apply adjust_appendrel_attrs
+        * to all the RestrictInfos at once.  This will result in returning
+        * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+        * any duplication, and it's a sufficiently narrow corner case that we
+        * shouldn't sweat too much over it anyway.
+        */
+       if (inner_appinfo)
+               result = (List *) adjust_appendrel_attrs(root, (Node *) result,
+                                                                                                inner_appinfo);
+
        return result;
 }
 
@@ -1248,7 +1358,9 @@ create_join_clause(PlannerInfo *root,
                                                                                leftem->em_expr,
                                                                                rightem->em_expr,
                                                                                bms_union(leftem->em_relids,
-                                                                                                 rightem->em_relids));
+                                                                                                 rightem->em_relids),
+                                                                               bms_union(leftem->em_nullable_relids,
+                                                                                          rightem->em_nullable_relids));
 
        /* Mark the clause as redundant, or not */
        rinfo->parent_ec = parent_ec;
@@ -1470,7 +1582,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
                                left_type,
                                right_type,
                                inner_datatype;
-       Relids          inner_relids;
+       Relids          inner_relids,
+                               inner_nullable_relids;
        ListCell   *lc1;
 
        Assert(is_opclause(rinfo->clause));
@@ -1497,6 +1610,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
                inner_datatype = left_type;
                inner_relids = rinfo->left_relids;
        }
+       inner_nullable_relids = bms_intersect(inner_relids,
+                                                                                 rinfo->nullable_relids);
 
        /* Scan EquivalenceClasses for a match to outervar */
        foreach(lc1, root->eq_classes)
@@ -1555,7 +1670,8 @@ reconsider_outer_join_clause(PlannerInfo *root, RestrictInfo *rinfo,
                                                                                                   cur_ec->ec_collation,
                                                                                                   innervar,
                                                                                                   cur_em->em_expr,
-                                                                                                  inner_relids);
+                                                                                                  bms_copy(inner_relids),
+                                                                                       bms_copy(inner_nullable_relids));
                        if (process_equivalence(root, newrinfo, true))
                                match = true;
                }
@@ -1589,7 +1705,9 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
                                left_type,
                                right_type;
        Relids          left_relids,
-                               right_relids;
+                               right_relids,
+                               left_nullable_relids,
+                               right_nullable_relids;
        ListCell   *lc1;
 
        /* Can't use an outerjoin_delayed clause here */
@@ -1605,6 +1723,10 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
        rightvar = (Expr *) get_rightop(rinfo->clause);
        left_relids = rinfo->left_relids;
        right_relids = rinfo->right_relids;
+       left_nullable_relids = bms_intersect(left_relids,
+                                                                                rinfo->nullable_relids);
+       right_nullable_relids = bms_intersect(right_relids,
+                                                                                 rinfo->nullable_relids);
 
        foreach(lc1, root->eq_classes)
        {
@@ -1690,7 +1812,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
                                                                                                           cur_ec->ec_collation,
                                                                                                           leftvar,
                                                                                                           cur_em->em_expr,
-                                                                                                          left_relids);
+                                                                                                          bms_copy(left_relids),
+                                                                                        bms_copy(left_nullable_relids));
                                if (process_equivalence(root, newrinfo, true))
                                        matchleft = true;
                        }
@@ -1703,7 +1826,8 @@ reconsider_full_join_clause(PlannerInfo *root, RestrictInfo *rinfo)
                                                                                                           cur_ec->ec_collation,
                                                                                                           rightvar,
                                                                                                           cur_em->em_expr,
-                                                                                                          right_relids);
+                                                                                                          bms_copy(right_relids),
+                                                                                       bms_copy(right_nullable_relids));
                                if (process_equivalence(root, newrinfo, true))
                                        matchright = true;
                        }
@@ -1783,7 +1907,7 @@ exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
 
 /*
  * add_child_rel_equivalences
- *       Search for EC members that reference (only) the parent_rel, and
+ *       Search for EC members that reference the parent_rel, and
  *       add transformed members referencing the child_rel.
  *
  * Note that this function won't be called at all unless we have at least some
@@ -1821,20 +1945,47 @@ add_child_rel_equivalences(PlannerInfo *root,
                {
                        EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
-                       if (cur_em->em_is_child)
-                               continue;               /* ignore children here */
+                       if (cur_em->em_is_const || cur_em->em_is_child)
+                               continue;               /* ignore consts and children here */
 
-                       /* Does it reference (only) parent_rel? */
-                       if (bms_equal(cur_em->em_relids, parent_rel->relids))
+                       /* Does it reference parent_rel? */
+                       if (bms_overlap(cur_em->em_relids, parent_rel->relids))
                        {
                                /* Yes, generate transformed child version */
                                Expr       *child_expr;
+                               Relids          new_relids;
+                               Relids          new_nullable_relids;
 
                                child_expr = (Expr *)
                                        adjust_appendrel_attrs(root,
                                                                                   (Node *) cur_em->em_expr,
                                                                                   appinfo);
-                               (void) add_eq_member(cur_ec, child_expr, child_rel->relids,
+
+                               /*
+                                * Transform em_relids to match.  Note we do *not* do
+                                * pull_varnos(child_expr) here, as for example the
+                                * transformation might have substituted a constant, but we
+                                * don't want the child member to be marked as constant.
+                                */
+                               new_relids = bms_difference(cur_em->em_relids,
+                                                                                       parent_rel->relids);
+                               new_relids = bms_add_members(new_relids, child_rel->relids);
+
+                               /*
+                                * And likewise for nullable_relids.  Note this code assumes
+                                * parent and child relids are singletons.
+                                */
+                               new_nullable_relids = cur_em->em_nullable_relids;
+                               if (bms_overlap(new_nullable_relids, parent_rel->relids))
+                               {
+                                       new_nullable_relids = bms_difference(new_nullable_relids,
+                                                                                                                parent_rel->relids);
+                                       new_nullable_relids = bms_add_members(new_nullable_relids,
+                                                                                                                 child_rel->relids);
+                               }
+
+                               (void) add_eq_member(cur_ec, child_expr,
+                                                                        new_relids, new_nullable_relids,
                                                                         true, cur_em->em_datatype);
                        }
                }
@@ -1845,12 +1996,12 @@ add_child_rel_equivalences(PlannerInfo *root,
 /*
  * mutate_eclass_expressions
  *       Apply an expression tree mutator to all expressions stored in
- *       equivalence classes.
+ *       equivalence classes (but ignore child exprs unless include_child_exprs).
  *
  * This is a bit of a hack ... it's currently needed only by planagg.c,
  * which needs to do a global search-and-replace of MIN/MAX Aggrefs
  * after eclasses are already set up.  Without changing the eclasses too,
- * subsequent matching of ORDER BY clauses would fail.
+ * subsequent matching of ORDER BY and DISTINCT clauses would fail.
  *
  * Note that we assume the mutation won't affect relation membership or any
  * other properties we keep track of (which is a bit bogus, but by the time
@@ -1860,7 +2011,8 @@ add_child_rel_equivalences(PlannerInfo *root,
 void
 mutate_eclass_expressions(PlannerInfo *root,
                                                  Node *(*mutator) (),
-                                                 void *context)
+                                                 void *context,
+                                                 bool include_child_exprs)
 {
        ListCell   *lc1;
 
@@ -1873,6 +2025,9 @@ mutate_eclass_expressions(PlannerInfo *root,
                {
                        EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
 
+                       if (cur_em->em_is_child && !include_child_exprs)
+                               continue;               /* ignore children unless requested */
+
                        cur_em->em_expr = (Expr *)
                                mutator((Node *) cur_em->em_expr, context);
                }
@@ -1881,33 +2036,43 @@ mutate_eclass_expressions(PlannerInfo *root,
 
 
 /*
- * generate_implied_equalities_for_indexcol
- *       Create EC-derived joinclauses usable with a specific index column.
+ * generate_implied_equalities_for_column
+ *       Create EC-derived joinclauses usable with a specific column.
+ *
+ * This is used by indxpath.c to extract potentially indexable joinclauses
+ * from ECs, and can be used by foreign data wrappers for similar purposes.
+ * We assume that only expressions in Vars of a single table are of interest,
+ * but the caller provides a callback function to identify exactly which
+ * such expressions it would like to know about.
  *
- * We assume that any given index column could appear in only one EC.
+ * We assume that any given table/index column could appear in only one EC.
  * (This should be true in all but the most pathological cases, and if it
  * isn't, we stop on the first match anyway.)  Therefore, what we return
- * is a redundant list of clauses equating the index column to each of
+ * is a redundant list of clauses equating the table/index column to each of
  * the other-relation values it is known to be equal to.  Any one of
- * these clauses can be used to create a parameterized indexscan, and there
- * is no value in using more than one.  (But it *is* worthwhile to create
+ * these clauses can be used to create a parameterized path, and there
+ * is no value in using more than one. (But it *is* worthwhile to create
  * a separate parameterized path for each one, since that leads to different
  * join orders.)
+ *
+ * The caller can pass a Relids set of rels we aren't interested in joining
+ * to, so as to save the work of creating useless clauses.
  */
 List *
-generate_implied_equalities_for_indexcol(PlannerInfo *root,
-                                                                                IndexOptInfo *index,
-                                                                                int indexcol)
+generate_implied_equalities_for_column(PlannerInfo *root,
+                                                                          RelOptInfo *rel,
+                                                                          ec_matches_callback_type callback,
+                                                                          void *callback_arg,
+                                                                          Relids prohibited_rels)
 {
        List       *result = NIL;
-       RelOptInfo *rel = index->rel;
        bool            is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
        Index           parent_relid;
        ListCell   *lc1;
 
        /* If it's a child rel, we'll need to know what its parent is */
        if (is_child_rel)
-               parent_relid = get_parent_relid(root, rel);
+               parent_relid = find_childrel_appendrelinfo(root, rel)->parent_relid;
        else
                parent_relid = 0;               /* not used, but keep compiler quiet */
 
@@ -1933,12 +2098,12 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
                        continue;
 
                /*
-                * Scan members, looking for a match to the indexable column.  Note
-                * that child EC members are considered, but only when they belong to
-                * the target relation.  (Unlike regular members, the same expression
+                * Scan members, looking for a match to the target column.      Note that
+                * child EC members are considered, but only when they belong to the
+                * target relation.  (Unlike regular members, the same expression
                 * could be a child member of more than one EC.  Therefore, it's
-                * potentially order-dependent which EC a child relation's index
-                * column gets matched to.  This is annoying but it only happens in
+                * potentially order-dependent which EC a child relation's target
+                * column gets matched to.      This is annoying but it only happens in
                 * corner cases, so for now we live with just reporting the first
                 * match.  See also get_eclass_for_sort_expr.)
                 */
@@ -1947,8 +2112,7 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
                {
                        cur_em = (EquivalenceMember *) lfirst(lc2);
                        if (bms_equal(cur_em->em_relids, rel->relids) &&
-                               eclass_member_matches_indexcol(cur_ec, cur_em,
-                                                                                          index, indexcol))
+                               callback(root, rel, cur_ec, cur_em, callback_arg))
                                break;
                        cur_em = NULL;
                }
@@ -1974,6 +2138,10 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
                                bms_overlap(other_em->em_relids, rel->relids))
                                continue;
 
+                       /* Forget it if caller doesn't want joins to this rel */
+                       if (bms_overlap(other_em->em_relids, prohibited_rels))
+                               continue;
+
                        /*
                         * Also, if this is a child rel, avoid generating a useless join
                         * to its parent rel.
@@ -2008,30 +2176,6 @@ generate_implied_equalities_for_indexcol(PlannerInfo *root,
        return result;
 }
 
-/*
- * get_parent_relid
- *             Get the relid of a child rel's parent appendrel
- *
- * Possibly this should be somewhere else, but right now the logic is only
- * needed here.
- */
-static Index
-get_parent_relid(PlannerInfo *root, RelOptInfo *rel)
-{
-       ListCell   *lc;
-
-       foreach(lc, root->append_rel_list)
-       {
-               AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
-
-               if (appinfo->child_relid == rel->relid)
-                       return appinfo->parent_relid;
-       }
-       /* should have found the entry ... */
-       elog(ERROR, "child rel not found in append_rel_list");
-       return 0;
-}
-
 /*
  * have_relevant_eclass_joinclause
  *             Detect whether there is an EquivalenceClass that could produce
@@ -2174,3 +2318,31 @@ eclass_useful_for_merging(EquivalenceClass *eclass,
 
        return false;
 }
+
+
+/*
+ * is_redundant_derived_clause
+ *             Test whether rinfo is derived from same EC as any clause in clauselist;
+ *             if so, it can be presumed to represent a condition that's redundant
+ *             with that member of the list.
+ */
+bool
+is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist)
+{
+       EquivalenceClass *parent_ec = rinfo->parent_ec;
+       ListCell   *lc;
+
+       /* Fail if it's not a potentially-redundant clause from some EC */
+       if (parent_ec == NULL)
+               return false;
+
+       foreach(lc, clauselist)
+       {
+               RestrictInfo *otherrinfo = (RestrictInfo *) lfirst(lc);
+
+               if (otherrinfo->parent_ec == parent_ec)
+                       return true;
+       }
+
+       return false;
+}