diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7997f50..a225414 100644
*** a/src/backend/optimizer/path/equivclass.c
--- b/src/backend/optimizer/path/equivclass.c
***************
*** 27,32 ****
--- 27,33 ----
  #include "optimizer/paths.h"
  #include "optimizer/planmain.h"
  #include "optimizer/prep.h"
+ #include "optimizer/restrictinfo.h"
  #include "optimizer/var.h"
  #include "utils/lsyscache.h"
  
*************** static bool reconsider_full_join_clause(
*** 71,78 ****
   *	  any delay by an outer join, so its two sides can be considered equal
   *	  anywhere they are both computable; moreover that equality can be
   *	  extended transitively.  Record this knowledge in the EquivalenceClass
!  *	  data structure.  Returns TRUE if successful, FALSE if not (in which
!  *	  case caller should treat the clause as ordinary, not an equivalence).
   *
   * If below_outer_join is true, then the clause was found below the nullable
   * side of an outer join, so its sides might validly be both NULL rather than
--- 72,85 ----
   *	  any delay by an outer join, so its two sides can be considered equal
   *	  anywhere they are both computable; moreover that equality can be
   *	  extended transitively.  Record this knowledge in the EquivalenceClass
!  *	  data structure, if applicable.  Returns TRUE if successful, FALSE if not
!  *	  (in which case caller should treat the clause as ordinary, not an
!  *	  equivalence).
!  *
!  * In some cases, although we cannot convert a clause into EquivalenceClass
!  * knowledge, we can still modify it to a more useful form than the original.
!  * Then, *p_restrictinfo will be replaced by a new RestrictInfo, which is what
!  * the caller should use for further processing.
   *
   * If below_outer_join is true, then the clause was found below the nullable
   * side of an outer join, so its sides might validly be both NULL rather than
*************** static bool reconsider_full_join_clause(
*** 104,112 ****
   * memory context.
   */
  bool
! process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
  					bool below_outer_join)
  {
  	Expr	   *clause = restrictinfo->clause;
  	Oid			opno,
  				collation,
--- 111,121 ----
   * memory context.
   */
  bool
! process_equivalence(PlannerInfo *root,
! 					RestrictInfo **p_restrictinfo,
  					bool below_outer_join)
  {
+ 	RestrictInfo *restrictinfo = *p_restrictinfo;
  	Expr	   *clause = restrictinfo->clause;
  	Oid			opno,
  				collation,
*************** process_equivalence(PlannerInfo *root, R
*** 154,169 ****
  									   collation);
  
  	/*
! 	 * Reject clauses of the form X=X.  These are not as redundant as they
! 	 * might seem at first glance: assuming the operator is strict, this is
! 	 * really an expensive way to write X IS NOT NULL.  So we must not risk
! 	 * just losing the clause, which would be possible if there is already a
! 	 * single-element EquivalenceClass containing X.  The case is not common
! 	 * enough to be worth contorting the EC machinery for, so just reject the
! 	 * clause and let it be processed as a normal restriction clause.
  	 */
  	if (equal(item1, item2))
! 		return false;			/* X=X is not a useful equivalence */
  
  	/*
  	 * If below outer join, check for strictness, else reject.
--- 163,207 ----
  									   collation);
  
  	/*
! 	 * Clauses of the form X=X cannot be translated into EquivalenceClasses.
! 	 * We'd either end up with a single-entry EC, losing the knowledge that
! 	 * the clause was present at all, or else make an EC with duplicate
! 	 * entries, causing other issues.
  	 */
  	if (equal(item1, item2))
! 	{
! 		/*
! 		 * If the operator is strict, then the clause can be treated as just
! 		 * "X IS NOT NULL".  (Since we know we are considering a top-level
! 		 * qual, we can ignore the difference between FALSE and NULL results.)
! 		 * It's worth making the conversion because we'll typically get a much
! 		 * better selectivity estimate than we would for X=X.
! 		 *
! 		 * If the operator is not strict, we can't be sure what it will do
! 		 * with NULLs, so don't attempt to optimize it.
! 		 */
! 		set_opfuncid((OpExpr *) clause);
! 		if (func_strict(((OpExpr *) clause)->opfuncid))
! 		{
! 			NullTest   *ntest = makeNode(NullTest);
! 
! 			ntest->arg = item1;
! 			ntest->nulltesttype = IS_NOT_NULL;
! 			ntest->argisrow = false;	/* correct even if composite arg */
! 			ntest->location = -1;
! 
! 			*p_restrictinfo =
! 				make_restrictinfo((Expr *) ntest,
! 								  restrictinfo->is_pushed_down,
! 								  restrictinfo->outerjoin_delayed,
! 								  restrictinfo->pseudoconstant,
! 								  restrictinfo->security_level,
! 								  NULL,
! 								  restrictinfo->outer_relids,
! 								  restrictinfo->nullable_relids);
! 		}
! 		return false;
! 	}
  
  	/*
  	 * If below outer join, check for strictness, else reject.
*************** reconsider_outer_join_clause(PlannerInfo
*** 1741,1747 ****
  												   bms_copy(inner_relids),
  												   bms_copy(inner_nullable_relids),
  												   cur_ec->ec_min_security);
! 			if (process_equivalence(root, newrinfo, true))
  				match = true;
  		}
  
--- 1779,1785 ----
  												   bms_copy(inner_relids),
  												   bms_copy(inner_nullable_relids),
  												   cur_ec->ec_min_security);
! 			if (process_equivalence(root, &newrinfo, true))
  				match = true;
  		}
  
*************** reconsider_full_join_clause(PlannerInfo 
*** 1884,1890 ****
  													   bms_copy(left_relids),
  													   bms_copy(left_nullable_relids),
  													   cur_ec->ec_min_security);
! 				if (process_equivalence(root, newrinfo, true))
  					matchleft = true;
  			}
  			eq_op = select_equality_operator(cur_ec,
--- 1922,1928 ----
  													   bms_copy(left_relids),
  													   bms_copy(left_nullable_relids),
  													   cur_ec->ec_min_security);
! 				if (process_equivalence(root, &newrinfo, true))
  					matchleft = true;
  			}
  			eq_op = select_equality_operator(cur_ec,
*************** reconsider_full_join_clause(PlannerInfo 
*** 1899,1905 ****
  													   bms_copy(right_relids),
  													   bms_copy(right_nullable_relids),
  													   cur_ec->ec_min_security);
! 				if (process_equivalence(root, newrinfo, true))
  					matchright = true;
  			}
  		}
--- 1937,1943 ----
  													   bms_copy(right_relids),
  													   bms_copy(right_nullable_relids),
  													   cur_ec->ec_min_security);
! 				if (process_equivalence(root, &newrinfo, true))
  					matchright = true;
  			}
  		}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 9931ddd..974eb58 100644
*** a/src/backend/optimizer/plan/initsplan.c
--- b/src/backend/optimizer/plan/initsplan.c
*************** distribute_qual_to_rels(PlannerInfo *roo
*** 1964,1973 ****
  		if (maybe_equivalence)
  		{
  			if (check_equivalence_delay(root, restrictinfo) &&
! 				process_equivalence(root, restrictinfo, below_outer_join))
  				return;
  			/* EC rejected it, so set left_ec/right_ec the hard way ... */
! 			initialize_mergeclause_eclasses(root, restrictinfo);
  			/* ... and fall through to distribute_restrictinfo_to_rels */
  		}
  		else if (maybe_outer_join && restrictinfo->can_join)
--- 1964,1974 ----
  		if (maybe_equivalence)
  		{
  			if (check_equivalence_delay(root, restrictinfo) &&
! 				process_equivalence(root, &restrictinfo, below_outer_join))
  				return;
  			/* EC rejected it, so set left_ec/right_ec the hard way ... */
! 			if (restrictinfo->mergeopfamilies)	/* EC might have changed this */
! 				initialize_mergeclause_eclasses(root, restrictinfo);
  			/* ... and fall through to distribute_restrictinfo_to_rels */
  		}
  		else if (maybe_outer_join && restrictinfo->can_join)
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index a15eee5..ea886b6 100644
*** a/src/include/optimizer/paths.h
--- b/src/include/optimizer/paths.h
*************** typedef bool (*ec_matches_callback_type)
*** 127,133 ****
  										  EquivalenceMember *em,
  										  void *arg);
  
! extern bool process_equivalence(PlannerInfo *root, RestrictInfo *restrictinfo,
  					bool below_outer_join);
  extern Expr *canonicalize_ec_expression(Expr *expr,
  						   Oid req_type, Oid req_collation);
--- 127,134 ----
  										  EquivalenceMember *em,
  										  void *arg);
  
! extern bool process_equivalence(PlannerInfo *root,
! 					RestrictInfo **p_restrictinfo,
  					bool below_outer_join);
  extern Expr *canonicalize_ec_expression(Expr *expr,
  						   Oid req_type, Oid req_collation);
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index a96b2a1..c448d85 100644
*** a/src/test/regress/expected/equivclass.out
--- b/src/test/regress/expected/equivclass.out
*************** reset session authorization;
*** 421,423 ****
--- 421,441 ----
  revoke select on ec0 from regress_user_ectest;
  revoke select on ec1 from regress_user_ectest;
  drop user regress_user_ectest;
+ -- check that X=X is converted to X IS NOT NULL when appropriate
+ explain (costs off)
+   select * from tenk1 where unique1 = unique1 and unique2 = unique2;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Seq Scan on tenk1
+    Filter: ((unique1 IS NOT NULL) AND (unique2 IS NOT NULL))
+ (2 rows)
+ 
+ -- this could be converted, but isn't at present
+ explain (costs off)
+   select * from tenk1 where unique1 = unique1 or unique2 = unique2;
+                        QUERY PLAN                       
+ --------------------------------------------------------
+  Seq Scan on tenk1
+    Filter: ((unique1 = unique1) OR (unique2 = unique2))
+ (2 rows)
+ 
diff --git a/src/test/regress/sql/equivclass.sql b/src/test/regress/sql/equivclass.sql
index 0e4aa0c..85aa65d 100644
*** a/src/test/regress/sql/equivclass.sql
--- b/src/test/regress/sql/equivclass.sql
*************** revoke select on ec0 from regress_user_e
*** 254,256 ****
--- 254,264 ----
  revoke select on ec1 from regress_user_ectest;
  
  drop user regress_user_ectest;
+ 
+ -- check that X=X is converted to X IS NOT NULL when appropriate
+ explain (costs off)
+   select * from tenk1 where unique1 = unique1 and unique2 = unique2;
+ 
+ -- this could be converted, but isn't at present
+ explain (costs off)
+   select * from tenk1 where unique1 = unique1 or unique2 = unique2;
