/*
  * add_child_rel_equivalences
- *   Search for EC members that reference the parent_rel, and
+ *   Search for EC members that reference the root parent of child_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
  *
  * parent_rel and child_rel could be derived from appinfo, but since the
  * caller has already computed them, we might as well just pass them in.
+ *
+ * The passed-in AppendRelInfo is not used when the parent_rel is not a
+ * top-level baserel, since it shows the mapping from the parent_rel but
+ * we need to translate EC expressions that refer to the top-level parent.
+ * Using it is faster than using adjust_appendrel_attrs_multilevel(), though,
+ * so we prefer it when we can.
  */
 void
 add_child_rel_equivalences(PlannerInfo *root,
                           RelOptInfo *parent_rel,
                           RelOptInfo *child_rel)
 {
+   Relids      top_parent_relids = child_rel->top_parent_relids;
+   Relids      child_relids = child_rel->relids;
    ListCell   *lc1;
 
+   Assert(IS_SIMPLE_REL(parent_rel));
+
    foreach(lc1, root->eq_classes)
    {
        EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
         * No point in searching if child's topmost parent rel is not
         * mentioned in eclass.
         */
-       if (!bms_is_subset(child_rel->top_parent_relids, cur_ec->ec_relids))
+       if (!bms_is_subset(top_parent_relids, cur_ec->ec_relids))
            continue;
 
        foreach(lc2, cur_ec->ec_members)
             * already-transformed child members.  Otherwise, if some original
             * member expression references more than one appendrel, we'd get
             * an O(N^2) explosion of useless derived expressions for
-            * combinations of children.
+            * combinations of children.  (But add_child_join_rel_equivalences
+            * may add targeted combinations for partitionwise-join purposes.)
             */
            if (cur_em->em_is_child)
                continue;       /* ignore children here */
 
            /* Does this member reference child's topmost parent rel? */
-           if (bms_overlap(cur_em->em_relids, child_rel->top_parent_relids))
+           if (bms_overlap(cur_em->em_relids, top_parent_relids))
            {
                /* Yes, generate transformed child version */
                Expr       *child_expr;
                    child_expr = (Expr *)
                        adjust_appendrel_attrs_multilevel(root,
                                                          (Node *) cur_em->em_expr,
-                                                         child_rel->relids,
-                                                         child_rel->top_parent_relids);
+                                                         child_relids,
+                                                         top_parent_relids);
                }
 
                /*
                 * don't want the child member to be marked as constant.
                 */
                new_relids = bms_difference(cur_em->em_relids,
-                                           child_rel->top_parent_relids);
-               new_relids = bms_add_members(new_relids, child_rel->relids);
+                                           top_parent_relids);
+               new_relids = bms_add_members(new_relids, child_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,
-                               child_rel->top_parent_relids))
+               if (bms_overlap(new_nullable_relids, top_parent_relids))
                {
                    new_nullable_relids = bms_difference(new_nullable_relids,
-                                                        child_rel->top_parent_relids);
+                                                        top_parent_relids);
                    new_nullable_relids = bms_add_members(new_nullable_relids,
-                                                         child_rel->relids);
+                                                         child_relids);
+               }
+
+               (void) add_eq_member(cur_ec, child_expr,
+                                    new_relids, new_nullable_relids,
+                                    true, cur_em->em_datatype);
+           }
+       }
+   }
+}
+
+/*
+ * add_child_join_rel_equivalences
+ *   Like add_child_rel_equivalences(), but for joinrels
+ *
+ * Here we find the ECs relevant to the top parent joinrel and add transformed
+ * member expressions that refer to this child joinrel.
+ *
+ * Note that this function won't be called at all unless we have at least some
+ * reason to believe that the EC members it generates will be useful.
+ */
+void
+add_child_join_rel_equivalences(PlannerInfo *root,
+                               int nappinfos, AppendRelInfo **appinfos,
+                               RelOptInfo *parent_joinrel,
+                               RelOptInfo *child_joinrel)
+{
+   Relids      top_parent_relids = child_joinrel->top_parent_relids;
+   Relids      child_relids = child_joinrel->relids;
+   ListCell   *lc1;
+
+   Assert(IS_JOIN_REL(child_joinrel) && IS_JOIN_REL(parent_joinrel));
+
+   foreach(lc1, root->eq_classes)
+   {
+       EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+       ListCell   *lc2;
+
+       /*
+        * If this EC contains a volatile expression, then generating child
+        * EMs would be downright dangerous, so skip it.  We rely on a
+        * volatile EC having only one EM.
+        */
+       if (cur_ec->ec_has_volatile)
+           continue;
+
+       /*
+        * No point in searching if child's topmost parent rel is not
+        * mentioned in eclass.
+        */
+       if (!bms_overlap(top_parent_relids, cur_ec->ec_relids))
+           continue;
+
+       foreach(lc2, cur_ec->ec_members)
+       {
+           EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+
+           if (cur_em->em_is_const)
+               continue;       /* ignore consts here */
+
+           /*
+            * We consider only original EC members here, not
+            * already-transformed child members.
+            */
+           if (cur_em->em_is_child)
+               continue;       /* ignore children here */
+
+           /*
+            * We may ignore expressions that reference a single baserel,
+            * because add_child_rel_equivalences should have handled them.
+            */
+           if (bms_membership(cur_em->em_relids) != BMS_MULTIPLE)
+               continue;
+
+           /* Does this member reference child's topmost parent rel? */
+           if (bms_overlap(cur_em->em_relids, top_parent_relids))
+           {
+               /* Yes, generate transformed child version */
+               Expr       *child_expr;
+               Relids      new_relids;
+               Relids      new_nullable_relids;
+
+               if (parent_joinrel->reloptkind == RELOPT_JOINREL)
+               {
+                   /* Simple single-level transformation */
+                   child_expr = (Expr *)
+                       adjust_appendrel_attrs(root,
+                                              (Node *) cur_em->em_expr,
+                                              nappinfos, appinfos);
                }
+               else
+               {
+                   /* Must do multi-level transformation */
+                   Assert(parent_joinrel->reloptkind == RELOPT_OTHER_JOINREL);
+                   child_expr = (Expr *)
+                       adjust_appendrel_attrs_multilevel(root,
+                                                         (Node *) cur_em->em_expr,
+                                                         child_relids,
+                                                         top_parent_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,
+                                           top_parent_relids);
+               new_relids = bms_add_members(new_relids, child_relids);
+
+               /*
+                * For nullable_relids, we must selectively replace parent
+                * nullable relids with child ones.
+                */
+               new_nullable_relids = cur_em->em_nullable_relids;
+               if (bms_overlap(new_nullable_relids, top_parent_relids))
+                   new_nullable_relids =
+                       adjust_child_relids_multilevel(root,
+                                                      new_nullable_relids,
+                                                      child_relids,
+                                                      top_parent_relids);
 
                (void) add_eq_member(cur_ec, child_expr,
                                     new_relids, new_nullable_relids,
 
  550 |     | 
 (12 rows)
 
+-- bug with inadequate sort key representation
+SET enable_partitionwise_aggregate TO true;
+SET enable_hashjoin TO false;
+EXPLAIN (COSTS OFF)
+SELECT a, b FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b)
+  WHERE a BETWEEN 490 AND 510
+  GROUP BY 1, 2 ORDER BY 1, 2;
+                                                    QUERY PLAN                                                     
+-------------------------------------------------------------------------------------------------------------------
+ Group
+   Group Key: (COALESCE(prt1_p1.a, p2.a)), (COALESCE(prt1_p1.b, p2.b))
+   ->  Merge Append
+         Sort Key: (COALESCE(prt1_p1.a, p2.a)), (COALESCE(prt1_p1.b, p2.b))
+         ->  Group
+               Group Key: (COALESCE(prt1_p1.a, p2.a)), (COALESCE(prt1_p1.b, p2.b))
+               ->  Sort
+                     Sort Key: (COALESCE(prt1_p1.a, p2.a)), (COALESCE(prt1_p1.b, p2.b))
+                     ->  Merge Full Join
+                           Merge Cond: ((prt1_p1.a = p2.a) AND (prt1_p1.b = p2.b))
+                           Filter: ((COALESCE(prt1_p1.a, p2.a) >= 490) AND (COALESCE(prt1_p1.a, p2.a) <= 510))
+                           ->  Sort
+                                 Sort Key: prt1_p1.a, prt1_p1.b
+                                 ->  Seq Scan on prt1_p1
+                           ->  Sort
+                                 Sort Key: p2.a, p2.b
+                                 ->  Seq Scan on prt2_p1 p2
+         ->  Group
+               Group Key: (COALESCE(prt1_p2.a, p2_1.a)), (COALESCE(prt1_p2.b, p2_1.b))
+               ->  Sort
+                     Sort Key: (COALESCE(prt1_p2.a, p2_1.a)), (COALESCE(prt1_p2.b, p2_1.b))
+                     ->  Merge Full Join
+                           Merge Cond: ((prt1_p2.a = p2_1.a) AND (prt1_p2.b = p2_1.b))
+                           Filter: ((COALESCE(prt1_p2.a, p2_1.a) >= 490) AND (COALESCE(prt1_p2.a, p2_1.a) <= 510))
+                           ->  Sort
+                                 Sort Key: prt1_p2.a, prt1_p2.b
+                                 ->  Seq Scan on prt1_p2
+                           ->  Sort
+                                 Sort Key: p2_1.a, p2_1.b
+                                 ->  Seq Scan on prt2_p2 p2_1
+         ->  Group
+               Group Key: (COALESCE(prt1_p3.a, p2_2.a)), (COALESCE(prt1_p3.b, p2_2.b))
+               ->  Sort
+                     Sort Key: (COALESCE(prt1_p3.a, p2_2.a)), (COALESCE(prt1_p3.b, p2_2.b))
+                     ->  Merge Full Join
+                           Merge Cond: ((prt1_p3.a = p2_2.a) AND (prt1_p3.b = p2_2.b))
+                           Filter: ((COALESCE(prt1_p3.a, p2_2.a) >= 490) AND (COALESCE(prt1_p3.a, p2_2.a) <= 510))
+                           ->  Sort
+                                 Sort Key: prt1_p3.a, prt1_p3.b
+                                 ->  Seq Scan on prt1_p3
+                           ->  Sort
+                                 Sort Key: p2_2.a, p2_2.b
+                                 ->  Seq Scan on prt2_p3 p2_2
+(43 rows)
+
+SELECT a, b FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b)
+  WHERE a BETWEEN 490 AND 510
+  GROUP BY 1, 2 ORDER BY 1, 2;
+  a  | b  
+-----+----
+ 490 | 15
+ 492 | 17
+ 494 | 19
+ 495 | 20
+ 496 | 21
+ 498 | 23
+ 500 |  0
+ 501 |  1
+ 502 |  2
+ 504 |  4
+ 506 |  6
+ 507 |  7
+ 508 |  8
+ 510 | 10
+(14 rows)
+
+RESET enable_partitionwise_aggregate;
+RESET enable_hashjoin;
 --
 -- partitioned by expression
 --