diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 690b753369..26c98198b5 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -840,10 +840,8 @@ find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel)
  * scanning of the quals and before Path construction begins.
  *
  * We make no attempt to avoid generating duplicate RestrictInfos here: we
- * don't search ec_sources for matches, nor put the created RestrictInfos
- * into ec_derives.  Doing so would require some slightly ugly changes in
- * initsplan.c's API, and there's no real advantage, because the clauses
- * generated here can't duplicate anything we will generate for joins anyway.
+ * don't search ec_sources or ec_derives for matches.  It doesn't really
+ * seem worth the trouble to do so.
  */
 void
 generate_base_implied_equalities(PlannerInfo *root)
@@ -969,6 +967,7 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 	{
 		EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
 		Oid			eq_op;
+		RestrictInfo *rinfo;
 
 		Assert(!cur_em->em_is_child);	/* no children yet */
 		if (cur_em == const_em)
@@ -982,14 +981,31 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 			ec->ec_broken = true;
 			break;
 		}
-		process_implied_equality(root, eq_op, ec->ec_collation,
-								 cur_em->em_expr, const_em->em_expr,
-								 bms_copy(ec->ec_relids),
-								 bms_union(cur_em->em_nullable_relids,
-										   const_em->em_nullable_relids),
-								 ec->ec_min_security,
-								 ec->ec_below_outer_join,
-								 cur_em->em_is_const);
+		rinfo = process_implied_equality(root, eq_op, ec->ec_collation,
+										 cur_em->em_expr, const_em->em_expr,
+										 bms_copy(ec->ec_relids),
+										 bms_union(cur_em->em_nullable_relids,
+												   const_em->em_nullable_relids),
+										 ec->ec_min_security,
+										 ec->ec_below_outer_join,
+										 cur_em->em_is_const);
+
+		/*
+		 * If the clause didn't degenerate to a constant, fill in the correct
+		 * markings for a mergejoinable clause, and save it in ec_derives. (We
+		 * will not re-use such clauses directly, but selectivity estimation
+		 * may consult the list later.  Note that this use of ec_derives does
+		 * not overlap with its use for join clauses, since we never generate
+		 * join clauses from an ec_has_const eclass.)
+		 */
+		if (rinfo && rinfo->mergeopfamilies)
+		{
+			/* it's not redundant, so don't set parent_ec */
+			rinfo->left_ec = rinfo->right_ec = ec;
+			rinfo->left_em = cur_em;
+			rinfo->right_em = const_em;
+			ec->ec_derives = lappend(ec->ec_derives, rinfo);
+		}
 	}
 }
 
@@ -1028,6 +1044,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 		{
 			EquivalenceMember *prev_em = prev_ems[relid];
 			Oid			eq_op;
+			RestrictInfo *rinfo;
 
 			eq_op = select_equality_operator(ec,
 											 prev_em->em_datatype,
@@ -1038,14 +1055,29 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 				ec->ec_broken = true;
 				break;
 			}
-			process_implied_equality(root, eq_op, ec->ec_collation,
-									 prev_em->em_expr, cur_em->em_expr,
-									 bms_copy(ec->ec_relids),
-									 bms_union(prev_em->em_nullable_relids,
-											   cur_em->em_nullable_relids),
-									 ec->ec_min_security,
-									 ec->ec_below_outer_join,
-									 false);
+			rinfo = process_implied_equality(root, eq_op, ec->ec_collation,
+											 prev_em->em_expr, cur_em->em_expr,
+											 bms_copy(ec->ec_relids),
+											 bms_union(prev_em->em_nullable_relids,
+													   cur_em->em_nullable_relids),
+											 ec->ec_min_security,
+											 ec->ec_below_outer_join,
+											 false);
+
+			/*
+			 * If the clause didn't degenerate to a constant, fill in the
+			 * correct markings for a mergejoinable clause.  We don't put it
+			 * in ec_derives however; we don't currently need to re-find such
+			 * clauses, and we don't want to clutter that list with non-join
+			 * clauses.
+			 */
+			if (rinfo && rinfo->mergeopfamilies)
+			{
+				/* it's not redundant, so don't set parent_ec */
+				rinfo->left_ec = rinfo->right_ec = ec;
+				rinfo->left_em = prev_em;
+				rinfo->right_em = cur_em;
+			}
 		}
 		prev_ems[relid] = cur_em;
 	}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index e978b491f6..d64b32d4ba 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -62,14 +62,12 @@ static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root,
 										   JoinType jointype, List *clause);
 static void compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause);
 static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
-									bool is_deduced,
 									bool below_outer_join,
 									JoinType jointype,
 									Index security_level,
 									Relids qualscope,
 									Relids ojscope,
 									Relids outerjoin_nonnullable,
-									Relids deduced_nullable_relids,
 									List **postponed_qual_list);
 static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
 								  Relids *nullable_relids_p, bool is_pushed_down);
@@ -815,9 +813,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 
 			if (bms_is_subset(pq->relids, *qualscope))
 				distribute_qual_to_rels(root, pq->qual,
-										false, below_outer_join, JOIN_INNER,
+										below_outer_join, JOIN_INNER,
 										root->qual_security_level,
-										*qualscope, NULL, NULL, NULL,
+										*qualscope, NULL, NULL,
 										NULL);
 			else
 				*postponed_qual_list = lappend(*postponed_qual_list, pq);
@@ -831,9 +829,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 			Node	   *qual = (Node *) lfirst(l);
 
 			distribute_qual_to_rels(root, qual,
-									false, below_outer_join, JOIN_INNER,
+									below_outer_join, JOIN_INNER,
 									root->qual_security_level,
-									*qualscope, NULL, NULL, NULL,
+									*qualscope, NULL, NULL,
 									postponed_qual_list);
 		}
 	}
@@ -1008,10 +1006,10 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 			Node	   *qual = (Node *) lfirst(l);
 
 			distribute_qual_to_rels(root, qual,
-									false, below_outer_join, j->jointype,
+									below_outer_join, j->jointype,
 									root->qual_security_level,
 									*qualscope,
-									ojscope, nonnullable_rels, NULL,
+									ojscope, nonnullable_rels,
 									postponed_qual_list);
 		}
 
@@ -1110,14 +1108,12 @@ process_security_barrier_quals(PlannerInfo *root,
 			 * than being pushed up to top of tree, which we don't want.
 			 */
 			distribute_qual_to_rels(root, qual,
-									false,
 									below_outer_join,
 									JOIN_INNER,
 									security_level,
 									qualscope,
 									qualscope,
 									NULL,
-									NULL,
 									NULL);
 		}
 		security_level++;
@@ -1581,7 +1577,6 @@ compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause)
  *	  as belonging to a higher join level, just add it to postponed_qual_list.
  *
  * 'clause': the qual clause to be distributed
- * 'is_deduced': true if the qual came from implied-equality deduction
  * 'below_outer_join': true if the qual is from a JOIN/ON that is below the
  *		nullable side of a higher-level outer join
  * 'jointype': type of join the qual is from (JOIN_INNER for a WHERE clause)
@@ -1593,8 +1588,6 @@ compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause)
  *		baserels appearing on the outer (nonnullable) side of the join
  *		(for FULL JOIN this includes both sides of the join, and must in fact
  *		equal qualscope)
- * 'deduced_nullable_relids': if is_deduced is true, the nullable relids to
- *		impute to the clause; otherwise NULL
  * 'postponed_qual_list': list of PostponedQual structs, which we can add
  *		this qual to if it turns out to belong to a higher join level.
  *		Can be NULL if caller knows postponement is impossible.
@@ -1603,23 +1596,17 @@ compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause)
  * 'ojscope' is needed if we decide to force the qual up to the outer-join
  * level, which will be ojscope not necessarily qualscope.
  *
- * In normal use (when is_deduced is false), at the time this is called,
- * root->join_info_list must contain entries for all and only those special
- * joins that are syntactically below this qual.  But when is_deduced is true,
- * we are adding new deduced clauses after completion of deconstruct_jointree,
- * so it cannot be assumed that root->join_info_list has anything to do with
- * qual placement.
+ * At the time this is called, root->join_info_list must contain entries for
+ * all and only those special joins that are syntactically below this qual.
  */
 static void
 distribute_qual_to_rels(PlannerInfo *root, Node *clause,
-						bool is_deduced,
 						bool below_outer_join,
 						JoinType jointype,
 						Index security_level,
 						Relids qualscope,
 						Relids ojscope,
 						Relids outerjoin_nonnullable,
-						Relids deduced_nullable_relids,
 						List **postponed_qual_list)
 {
 	Relids		relids;
@@ -1653,7 +1640,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 
 		Assert(root->hasLateralRTEs);	/* shouldn't happen otherwise */
 		Assert(jointype == JOIN_INNER); /* mustn't postpone past outer join */
-		Assert(!is_deduced);	/* shouldn't be deduced, either */
 		pq->qual = clause;
 		pq->relids = relids;
 		*postponed_qual_list = lappend(*postponed_qual_list, pq);
@@ -1754,24 +1740,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 	 * This seems like another reason why it should perhaps be rethought.
 	 *----------
 	 */
-	if (is_deduced)
-	{
-		/*
-		 * If the qual came from implied-equality deduction, it should not be
-		 * outerjoin-delayed, else deducer blew it.  But we can't check this
-		 * because the join_info_list may now contain OJs above where the qual
-		 * belongs.  For the same reason, we must rely on caller to supply the
-		 * correct nullable_relids set.
-		 */
-		Assert(!ojscope);
-		is_pushed_down = true;
-		outerjoin_delayed = false;
-		nullable_relids = deduced_nullable_relids;
-		/* Don't feed it back for more deductions */
-		maybe_equivalence = false;
-		maybe_outer_join = false;
-	}
-	else if (bms_overlap(relids, outerjoin_nonnullable))
+	if (bms_overlap(relids, outerjoin_nonnullable))
 	{
 		/*
 		 * The qual is attached to an outer join and mentions (some of the)
@@ -2277,14 +2246,18 @@ distribute_restrictinfo_to_rels(PlannerInfo *root,
  * can produce constant TRUE or constant FALSE.  (Otherwise it's not,
  * because the expressions went through eval_const_expressions already.)
  *
+ * Returns the generated RestrictInfo, if any.  The result will be NULL
+ * if both_const is true and we successfully reduced the clause to
+ * constant TRUE.
+ *
  * Note: this function will copy item1 and item2, but it is caller's
  * responsibility to make sure that the Relids parameters are fresh copies
  * not shared with other uses.
  *
- * This is currently used only when an EquivalenceClass is found to
- * contain pseudoconstants.  See path/pathkeys.c for more details.
+ * Note: we do not do initialize_mergeclause_eclasses() here.  It is
+ * caller's responsibility that left_ec/right_ec be set as necessary.
  */
-void
+RestrictInfo *
 process_implied_equality(PlannerInfo *root,
 						 Oid opno,
 						 Oid collation,
@@ -2296,24 +2269,27 @@ process_implied_equality(PlannerInfo *root,
 						 bool below_outer_join,
 						 bool both_const)
 {
-	Expr	   *clause;
+	RestrictInfo *restrictinfo;
+	Node	   *clause;
+	Relids		relids;
+	bool		pseudoconstant = false;
 
 	/*
 	 * Build the new clause.  Copy to ensure it shares no substructure with
 	 * original (this is necessary in case there are subselects in there...)
 	 */
-	clause = make_opclause(opno,
-						   BOOLOID, /* opresulttype */
-						   false,	/* opretset */
-						   copyObject(item1),
-						   copyObject(item2),
-						   InvalidOid,
-						   collation);
+	clause = (Node *) make_opclause(opno,
+									BOOLOID,	/* opresulttype */
+									false,	/* opretset */
+									copyObject(item1),
+									copyObject(item2),
+									InvalidOid,
+									collation);
 
 	/* If both constant, try to reduce to a boolean constant. */
 	if (both_const)
 	{
-		clause = (Expr *) eval_const_expressions(root, (Node *) clause);
+		clause = eval_const_expressions(root, clause);
 
 		/* If we produced const TRUE, just drop the clause */
 		if (clause && IsA(clause, Const))
@@ -2322,25 +2298,106 @@ process_implied_equality(PlannerInfo *root,
 
 			Assert(cclause->consttype == BOOLOID);
 			if (!cclause->constisnull && DatumGetBool(cclause->constvalue))
-				return;
+				return NULL;
+		}
+	}
+
+	/*
+	 * The rest of this is a very cut-down version of distribute_qual_to_rels.
+	 * We can skip most of the work therein, but there are a couple of special
+	 * cases we still have to handle.
+	 *
+	 * Retrieve all relids mentioned within the possibly-simplified clause.
+	 */
+	relids = pull_varnos(clause);
+	Assert(bms_is_subset(relids, qualscope));
+
+	/*
+	 * If the clause is variable-free, our normal heuristic for pushing it
+	 * down to just the mentioned rels doesn't work, because there are none.
+	 * Apply at the given qualscope, or at the top of tree if it's nonvolatile
+	 * (which it very likely is, but we'll check, just to be sure).
+	 */
+	if (bms_is_empty(relids))
+	{
+		/* eval at original syntactic level */
+		relids = bms_copy(qualscope);
+		if (!contain_volatile_functions(clause))
+		{
+			/* mark as gating qual */
+			pseudoconstant = true;
+			/* tell createplan.c to check for gating quals */
+			root->hasPseudoConstantQuals = true;
+			/* if not below outer join, push it to top of tree */
+			if (!below_outer_join)
+			{
+				relids =
+					get_relids_in_jointree((Node *) root->parse->jointree,
+										   false);
+			}
 		}
 	}
 
+	/*
+	 * Build the RestrictInfo node itself.
+	 */
+	restrictinfo = make_restrictinfo((Expr *) clause,
+									 true,	/* is_pushed_down */
+									 false, /* outerjoin_delayed */
+									 pseudoconstant,
+									 security_level,
+									 relids,
+									 NULL,	/* outer_relids */
+									 nullable_relids);
+
+	/*
+	 * If it's a join clause, add vars used in the clause to targetlists of
+	 * their relations, so that they will be emitted by the plan nodes that
+	 * scan those relations (else they won't be available at the join node!).
+	 *
+	 * Typically, we'd have already done this when the component expressions
+	 * were first seen by distribute_qual_to_rels; but it is possible that
+	 * some of the Vars could have missed having that done because they only
+	 * appeared in single-relation clauses originally.  So do it here for
+	 * safety.
+	 */
+	if (bms_membership(relids) == BMS_MULTIPLE)
+	{
+		List	   *vars = pull_var_clause(clause,
+										   PVC_RECURSE_AGGREGATES |
+										   PVC_RECURSE_WINDOWFUNCS |
+										   PVC_INCLUDE_PLACEHOLDERS);
+
+		add_vars_to_targetlist(root, vars, relids, false);
+		list_free(vars);
+	}
+
+	/*
+	 * Check mergejoinability.  This will usually succeed, since the op came
+	 * from an EquivalenceClass; but we could have reduced the original clause
+	 * to a constant.
+	 */
+	check_mergejoinable(restrictinfo);
+
+	/*
+	 * Note we don't do initialize_mergeclause_eclasses(); the caller can
+	 * handle that much more cheaply than we can.  It's okay to call
+	 * distribute_restrictinfo_to_rels() before that happens.
+	 */
+
 	/*
 	 * Push the new clause into all the appropriate restrictinfo lists.
 	 */
-	distribute_qual_to_rels(root, (Node *) clause,
-							true, below_outer_join, JOIN_INNER,
-							security_level,
-							qualscope, NULL, NULL, nullable_relids,
-							NULL);
+	distribute_restrictinfo_to_rels(root, restrictinfo);
+
+	return restrictinfo;
 }
 
 /*
  * build_implied_join_equality --- build a RestrictInfo for a derived equality
  *
  * This overlaps the functionality of process_implied_equality(), but we
- * must return the RestrictInfo, not push it into the joininfo tree.
+ * must not push the RestrictInfo into the joininfo tree.
  *
  * Note: this function will copy item1 and item2, but it is caller's
  * responsibility to make sure that the Relids parameters are fresh copies
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index f3cefe67b8..81c4a7e560 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -77,16 +77,16 @@ extern void create_lateral_join_info(PlannerInfo *root);
 extern List *deconstruct_jointree(PlannerInfo *root);
 extern void distribute_restrictinfo_to_rels(PlannerInfo *root,
 											RestrictInfo *restrictinfo);
-extern void process_implied_equality(PlannerInfo *root,
-									 Oid opno,
-									 Oid collation,
-									 Expr *item1,
-									 Expr *item2,
-									 Relids qualscope,
-									 Relids nullable_relids,
-									 Index security_level,
-									 bool below_outer_join,
-									 bool both_const);
+extern RestrictInfo *process_implied_equality(PlannerInfo *root,
+											  Oid opno,
+											  Oid collation,
+											  Expr *item1,
+											  Expr *item2,
+											  Relids qualscope,
+											  Relids nullable_relids,
+											  Index security_level,
+											  bool below_outer_join,
+											  bool both_const);
 extern RestrictInfo *build_implied_join_equality(Oid opno,
 												 Oid collation,
 												 Expr *item1,
