diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 012c14b..0964d82 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1976,6 +1976,17 @@ _outEquivalenceMember(StringInfo str, const EquivalenceMember *node)
 }
 
 static void
+_outEquivalenceFilter(StringInfo str, const EquivalenceFilter *node)
+{
+	WRITE_NODE_TYPE("EQUIVALENCEFILTER");
+
+	WRITE_NODE_FIELD(ef_const);
+	WRITE_OID_FIELD(ef_opno);
+	WRITE_BOOL_FIELD(ef_const_is_left);
+	WRITE_UINT_FIELD(ef_source_rel);
+}
+
+static void
 _outPathKey(StringInfo str, const PathKey *node)
 {
 	WRITE_NODE_TYPE("PATHKEY");
@@ -3339,6 +3350,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_EquivalenceMember:
 				_outEquivalenceMember(str, obj);
 				break;
+			case T_EquivalenceFilter:
+				_outEquivalenceFilter(str, obj);
+				break;
 			case T_PathKey:
 				_outPathKey(str, obj);
 				break;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 48be45d..16193d9 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -17,6 +17,7 @@
 #include "postgres.h"
 
 #include "access/stratnum.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -840,6 +841,37 @@ generate_base_implied_equalities_const(PlannerInfo *root,
 }
 
 /*
+ * finds the opfamily and strategy number for the specified 'opno' and 'method'
+ * access method. Returns True if one is found and sets 'family' and
+ * 'amstrategy', or returns False if none are found.
+ */
+static bool
+find_am_family_and_stategy(Oid opno, Oid method, Oid *family, int *amstrategy)
+{
+	List *opfamilies;
+	ListCell *l;
+	int strategy;
+
+	opfamilies = get_opfamilies(opno, method);
+
+	foreach(l, opfamilies)
+	{
+		Oid opfamily = lfirst_oid(l);
+
+		strategy = get_op_opfamily_strategy(opno, opfamily);
+
+		if (strategy)
+		{
+			*amstrategy = strategy;
+			*family = opfamily;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
  * generate_base_implied_equalities when EC contains no pseudoconstants
  */
 static void
@@ -848,6 +880,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 {
 	EquivalenceMember **prev_ems;
 	ListCell   *lc;
+	ListCell   *lc2;
 
 	/*
 	 * We scan the EC members once and track the last-seen member for each
@@ -893,6 +926,56 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
 									 ec->ec_below_outer_join,
 									 false);
 		}
+
+		/*
+		 * Also push any EquivalenceFilter clauses down into all relations
+		 * other than the one which the filter actually originated from.
+		 */
+		foreach(lc2, ec->ec_filters)
+		{
+			EquivalenceFilter *ef = (EquivalenceFilter *) lfirst(lc2);
+			Expr *leftexpr;
+			Expr *rightexpr;
+			int strategy;
+			Oid opno;
+			Oid family;
+
+			if (ef->ef_source_rel == relid)
+				continue;
+
+			if (!find_am_family_and_stategy(ef->ef_opno, BTREE_AM_OID,
+				&family, &strategy))
+				continue;
+
+			if (ef->ef_const_is_left)
+			{
+				leftexpr = (Expr *) ef->ef_const;
+				rightexpr = cur_em->em_expr;
+			}
+			else
+			{
+				leftexpr = cur_em->em_expr;
+				rightexpr = (Expr *) ef->ef_const;
+			}
+
+			opno = get_opfamily_member(family,
+										exprType((Node *) leftexpr),
+										exprType((Node *) rightexpr),
+										strategy);
+
+			if (opno == InvalidOid)
+				continue;
+
+			process_implied_equality(root, opno,
+										ec->ec_collation,
+										leftexpr,
+										rightexpr,
+										bms_copy(ec->ec_relids),
+										bms_copy(cur_em->em_nullable_relids),
+										ec->ec_below_outer_join,
+										false);
+		}
+
 		prev_ems[relid] = cur_em;
 	}
 
@@ -1384,6 +1467,104 @@ create_join_clause(PlannerInfo *root,
 	return rinfo;
 }
 
+/*
+ * distribute_filter_quals_to_eclass
+ *		For each OpExpr in quallist look for an eclass which has an Expr
+ *		matching the Expr in the OpExpr. If a match is found we add a new
+ *		EquivalenceFilter to the eclass containing the filter details.
+ */
+void
+distribute_filter_quals_to_eclass(PlannerInfo *root, List *quallist)
+{
+	ListCell *l;
+
+	/* fast path for when no eclasses have been generated */
+	if (root->eq_classes == NIL)
+		return;
+
+	/*
+	 * For each qual in quallist try and find an eclass which contains the
+	 * non-Const part of the OpExpr. We'll tag any matches that we find onto
+	 * the correct eclass.
+	 */
+	foreach(l, quallist)
+	{
+		OpExpr	   *opexpr = (OpExpr *) lfirst(l);
+		Expr	   *leftexpr = (Expr *) linitial(opexpr->args);
+		Expr	   *rightexpr = (Expr *) lsecond(opexpr->args);
+		Const	   *constexpr;
+		Expr	   *varexpr;
+		Relids		exprrels;
+		int			relid;
+		bool		const_isleft;
+		ListCell *l2;
+
+		/*
+		 * Determine if the the OpExpr is in the form "expr op const" or
+		 * "const op expr".
+		 */
+		if (IsA(leftexpr, Const))
+		{
+			constexpr = (Const *) leftexpr;
+			varexpr = rightexpr;
+			const_isleft = true;
+		}
+		else
+		{
+			constexpr = (Const *) rightexpr;
+			varexpr = leftexpr;
+			const_isleft = false;
+		}
+
+		exprrels = pull_varnos((Node *) varexpr);
+
+		/* should be filtered out, but we need to determine relid anyway */
+		if (!bms_get_singleton_member(exprrels, &relid))
+			continue;
+
+		/* search for a matching eclass member in all eclasses */
+		foreach(l2, root->eq_classes)
+		{
+			EquivalenceClass *ec = (EquivalenceClass *) lfirst(l2);
+			ListCell *l3;
+
+			if (ec->ec_broken || ec->ec_has_volatile)
+				continue;
+
+			/*
+			 * if the eclass has a const then that const will serve as the
+			 * filter, we needn't add any others.
+			 */
+			if (ec->ec_has_const)
+				continue;
+
+			/* skip this eclass no members exist which belong to this relid */
+			if (!bms_is_member(relid, ec->ec_relids))
+				continue;
+
+			foreach(l3, ec->ec_members)
+			{
+				EquivalenceMember *em = (EquivalenceMember *) lfirst(l3);
+
+				if (!bms_is_member(relid, em->em_relids))
+					continue;
+
+				if (equal(em->em_expr, varexpr))
+				{
+					EquivalenceFilter *efilter;
+					efilter = makeNode(EquivalenceFilter);
+					efilter->ef_const = (Const *) copyObject(constexpr);
+					efilter->ef_const_is_left = const_isleft;
+					efilter->ef_opno = opexpr->opno;
+					efilter->ef_source_rel = relid;
+
+					ec->ec_filters = lappend(ec->ec_filters, efilter);
+					break;		/* Onto the next eclass */
+				}
+			}
+		}
+	}
+}
 
 /*
  * reconsider_outer_join_clauses
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 2f4e818..93f340b 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -51,7 +51,7 @@ static void add_lateral_info(PlannerInfo *root, Relids lhs, Relids rhs);
 static List *deconstruct_recurse(PlannerInfo *root, Node *jtnode,
 					bool below_outer_join,
 					Relids *qualscope, Relids *inner_join_rels,
-					List **postponed_qual_list);
+					List **postponed_qual_list, List **filter_qual_list);
 static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root,
 				   Relids left_rels, Relids right_rels,
 				   Relids inner_join_rels,
@@ -65,7 +65,9 @@ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 						Relids ojscope,
 						Relids outerjoin_nonnullable,
 						Relids deduced_nullable_relids,
-						List **postponed_qual_list);
+						List **postponed_qual_list,
+						List **filter_qual_list);
+static bool is_simple_filter_qual(OpExpr *expr);
 static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p,
 					  Relids *nullable_relids_p, bool is_pushed_down);
 static bool check_equivalence_delay(PlannerInfo *root,
@@ -648,6 +650,7 @@ deconstruct_jointree(PlannerInfo *root)
 	Relids		qualscope;
 	Relids		inner_join_rels;
 	List	   *postponed_qual_list = NIL;
+	List	   *filter_qual_list = NIL;
 
 	/* Start recursion at top of jointree */
 	Assert(root->parse->jointree != NULL &&
@@ -658,11 +661,14 @@ deconstruct_jointree(PlannerInfo *root)
 
 	result = deconstruct_recurse(root, (Node *) root->parse->jointree, false,
 								 &qualscope, &inner_join_rels,
-								 &postponed_qual_list);
+								 &postponed_qual_list, &filter_qual_list);
 
 	/* Shouldn't be any leftover quals */
 	Assert(postponed_qual_list == NIL);
 
+	/* try and match each filter_qual_list item up with an eclass. */
+	distribute_filter_quals_to_eclass(root, filter_qual_list);
+
 	return result;
 }
 
@@ -683,6 +689,8 @@ deconstruct_jointree(PlannerInfo *root)
  *		or free this, either)
  *	*postponed_qual_list is a list of PostponedQual structs, which we can
  *		add quals to if they turn out to belong to a higher join level
+ *	*filter_qual_list is appended to with a list of quals which may be useful
+ *		include as EquivalenceFilters.
  *	Return value is the appropriate joinlist for this jointree node
  *
  * In addition, entries will be added to root->join_info_list for outer joins.
@@ -690,7 +698,7 @@ deconstruct_jointree(PlannerInfo *root)
 static List *
 deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 					Relids *qualscope, Relids *inner_join_rels,
-					List **postponed_qual_list)
+					List **postponed_qual_list, List **filter_qual_list)
 {
 	List	   *joinlist;
 
@@ -737,7 +745,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 											   below_outer_join,
 											   &sub_qualscope,
 											   inner_join_rels,
-											   &child_postponed_quals);
+											   &child_postponed_quals,
+											   filter_qual_list);
 			*qualscope = bms_add_members(*qualscope, sub_qualscope);
 			sub_members = list_length(sub_joinlist);
 			remaining--;
@@ -770,7 +779,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 				distribute_qual_to_rels(root, pq->qual,
 										false, below_outer_join, JOIN_INNER,
 										*qualscope, NULL, NULL, NULL,
-										NULL);
+										NULL, filter_qual_list);
 			else
 				*postponed_qual_list = lappend(*postponed_qual_list, pq);
 		}
@@ -785,7 +794,7 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 			distribute_qual_to_rels(root, qual,
 									false, below_outer_join, JOIN_INNER,
 									*qualscope, NULL, NULL, NULL,
-									postponed_qual_list);
+									postponed_qual_list, filter_qual_list);
 		}
 	}
 	else if (IsA(jtnode, JoinExpr))
@@ -823,11 +832,13 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 				leftjoinlist = deconstruct_recurse(root, j->larg,
 												   below_outer_join,
 												   &leftids, &left_inners,
-												   &child_postponed_quals);
+												   &child_postponed_quals,
+												   filter_qual_list);
 				rightjoinlist = deconstruct_recurse(root, j->rarg,
 													below_outer_join,
 													&rightids, &right_inners,
-													&child_postponed_quals);
+													&child_postponed_quals,
+													filter_qual_list);
 				*qualscope = bms_union(leftids, rightids);
 				*inner_join_rels = *qualscope;
 				/* Inner join adds no restrictions for quals */
@@ -840,11 +851,13 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 				leftjoinlist = deconstruct_recurse(root, j->larg,
 												   below_outer_join,
 												   &leftids, &left_inners,
-												   &child_postponed_quals);
+												   &child_postponed_quals,
+												   filter_qual_list);
 				rightjoinlist = deconstruct_recurse(root, j->rarg,
 													true,
 													&rightids, &right_inners,
-													&child_postponed_quals);
+													&child_postponed_quals,
+													filter_qual_list);
 				*qualscope = bms_union(leftids, rightids);
 				*inner_join_rels = bms_union(left_inners, right_inners);
 				nonnullable_rels = leftids;
@@ -854,11 +867,13 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 				leftjoinlist = deconstruct_recurse(root, j->larg,
 												   below_outer_join,
 												   &leftids, &left_inners,
-												   &child_postponed_quals);
+												   &child_postponed_quals,
+												   filter_qual_list);
 				rightjoinlist = deconstruct_recurse(root, j->rarg,
 													below_outer_join,
 													&rightids, &right_inners,
-													&child_postponed_quals);
+													&child_postponed_quals,
+													filter_qual_list);
 				*qualscope = bms_union(leftids, rightids);
 				*inner_join_rels = bms_union(left_inners, right_inners);
 				/* Semi join adds no restrictions for quals */
@@ -875,11 +890,13 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 				leftjoinlist = deconstruct_recurse(root, j->larg,
 												   true,
 												   &leftids, &left_inners,
-												   &child_postponed_quals);
+												   &child_postponed_quals,
+												   filter_qual_list);
 				rightjoinlist = deconstruct_recurse(root, j->rarg,
 													true,
 													&rightids, &right_inners,
-													&child_postponed_quals);
+													&child_postponed_quals,
+													filter_qual_list);
 				*qualscope = bms_union(leftids, rightids);
 				*inner_join_rels = bms_union(left_inners, right_inners);
 				/* each side is both outer and inner */
@@ -963,7 +980,8 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join,
 									false, below_outer_join, j->jointype,
 									*qualscope,
 									ojscope, nonnullable_rels, NULL,
-									postponed_qual_list);
+									postponed_qual_list,
+									filter_qual_list);
 		}
 
 		/* Now we can add the SpecialJoinInfo to join_info_list */
@@ -1485,7 +1503,8 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 						Relids ojscope,
 						Relids outerjoin_nonnullable,
 						Relids deduced_nullable_relids,
-						List **postponed_qual_list)
+						List **postponed_qual_list,
+						List **filter_qual_list)
 {
 	Relids		relids;
 	bool		is_pushed_down;
@@ -1848,6 +1867,48 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause,
 
 	/* No EC special case applies, so push it into the clause lists */
 	distribute_restrictinfo_to_rels(root, restrictinfo);
+
+	/* Check if the qual looks useful to harvest as an EquivalenceFilter */
+	if (filter_qual_list != NULL && is_simple_filter_qual((OpExpr *) clause))
+		*filter_qual_list = lappend(*filter_qual_list, clause);
+}
+
+/*
+ * is_simple_filter_qual
+ *		Analyzes an OpExpr to determine if it may be useful as an
+ *		EquivalenceFilter. Returns true if the OpExpr may be of some use, or
+ *		false if it should not be used.
+ */
+static bool
+is_simple_filter_qual(OpExpr *expr)
+{
+	Expr *leftexpr;
+	Expr *rightexpr;
+
+	if (!IsA(expr, OpExpr))
+		return false;
+
+	if (list_length(expr->args) != 2)
+		return false;
+
+	leftexpr = (Expr *) linitial(expr->args);
+	rightexpr = (Expr *) lsecond(expr->args);
+
+	/* XXX should we restrict these to simple Var op Const expressions? */
+	if (IsA(leftexpr, Const))
+	{
+		if (bms_membership(pull_varnos((Node *) rightexpr)) == BMS_SINGLETON &&
+			!contain_volatile_functions((Node *) rightexpr))
+			return true;
+	}
+	else if (IsA(rightexpr, Const))
+	{
+		if (bms_membership(pull_varnos((Node *) leftexpr)) == BMS_SINGLETON &&
+			!contain_volatile_functions((Node *) leftexpr))
+			return true;
+	}
+
+	return false;
 }
 
 /*
@@ -2183,7 +2244,7 @@ process_implied_equality(PlannerInfo *root,
 	distribute_qual_to_rels(root, (Node *) clause,
 							true, below_outer_join, JOIN_INNER,
 							qualscope, NULL, NULL, nullable_relids,
-							NULL);
+							NULL, NULL);
 }
 
 /*
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 093da76..7e05e72 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -340,6 +340,34 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
 }
 
 /*
+ * get_opfamilies
+ *		Returns a list of Oids of each opfamily which 'opno' belonging to
+ *		'method' access method.
+ */
+List *
+get_opfamilies(Oid opno, Oid method)
+{
+	List	   *result = NIL;
+	CatCList   *catlist;
+	int			i;
+
+	catlist = SearchSysCacheList1(AMOPOPID, ObjectIdGetDatum(opno));
+
+	for (i = 0; i < catlist->n_members; i++)
+	{
+		HeapTuple	tuple = &catlist->members[i]->tuple;
+		Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+		if (aform->amopmethod == method)
+			result = lappend_oid(result, aform->amopfamily);
+	}
+
+	ReleaseSysCacheList(catlist);
+
+	return result;
+}
+
+/*
  * get_mergejoin_opfamilies
  *		Given a putatively mergejoinable operator, return a list of the OIDs
  *		of the btree opfamilies in which it represents equality.
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 94bdb7c..c463ebf 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -243,6 +243,7 @@ typedef enum NodeTag
 	T_GatherPath,
 	T_EquivalenceClass,
 	T_EquivalenceMember,
+	T_EquivalenceFilter,
 	T_PathKey,
 	T_RestrictInfo,
 	T_PlaceHolderVar,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 9a0dd28..07119a5 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -619,6 +619,8 @@ typedef struct EquivalenceClass
 	List	   *ec_members;		/* list of EquivalenceMembers */
 	List	   *ec_sources;		/* list of generating RestrictInfos */
 	List	   *ec_derives;		/* list of derived RestrictInfos */
+	List	   *ec_filters;		/* list of OpExprs may be pushed into other
+								 * relations as filters. */
 	Relids		ec_relids;		/* all relids appearing in ec_members, except
 								 * for child members (see below) */
 	bool		ec_has_const;	/* any pseudoconstants in ec_members? */
@@ -671,6 +673,42 @@ typedef struct EquivalenceMember
 } EquivalenceMember;
 
 /*
+ * EquivalenceFilter - List of filters on Consts which belong to the
+ * EquivalenceClass.
+ *
+ * When building the equivalence classes we also collected a list of quals in
+ * the form of; "Expr op Const" and "Const op Expr". These are collected in the
+ * hope that we'll later generate an equivalence class which contains the
+ * "Expr" part. For example, if we parse a query such as;
+ *
+ *		SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.id WHERE t1.id < 10;
+ *
+ * then since we'll end up with an equivalence class containing {t1.id,t2.id},
+ * we'll tag the "< 10" filter onto the eclass. We are able to do this because
+ * the eclass proves equality between each class member, therefore all members
+ * must be below 10.
+ *
+ * EquivalenceFilters store the details required to allow us to push these
+ * filter clauses down into other relations which share an equivalence class
+ * containing a member which matches the expression of this EquivalenceFilter.
+ *
+ * ef_const is the Const value which this filter should filter against.
+ * ef_opno is the operator to filter on.
+ * ef_const_is_left marks if the OpExpr was in the form "Const op Expr" or
+ * "Expr op Const".
+ * ef_source_rel is the relation id of where this qual originated from.
+ */
+typedef struct EquivalenceFilter
+{
+	NodeTag		type;
+
+	Const	   *ef_const;		/* the constant expression to filter on */
+	Oid			ef_opno;		/* Operator Oid of filter operator */
+	bool		ef_const_is_left; /* Is the Const on the left of the OpExrp? */
+	Index		ef_source_rel;	/* relid of originating relation. */
+} EquivalenceFilter;
+
+/*
  * PathKeys
  *
  * The sort ordering of a path is represented by a list of PathKey nodes.
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 87123a5..6eaf587 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -113,6 +113,8 @@ 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);
+extern void distribute_filter_quals_to_eclass(PlannerInfo *root,
+											  List *quallist);
 extern void reconsider_outer_join_clauses(PlannerInfo *root);
 extern EquivalenceClass *get_eclass_for_sort_expr(PlannerInfo *root,
 						 Expr *expr,
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index dcc421f..41bbbd7 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -52,6 +52,7 @@ extern bool get_ordering_op_properties(Oid opno,
 						   Oid *opfamily, Oid *opcintype, int16 *strategy);
 extern Oid	get_equality_op_for_ordering_op(Oid opno, bool *reverse);
 extern Oid	get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type);
+extern List *get_opfamilies(Oid opno, Oid method);
 extern List *get_mergejoin_opfamilies(Oid opno);
 extern bool get_compatible_hash_operators(Oid opno,
 							  Oid *lhs_opno, Oid *rhs_opno);
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index 0391b8e..7608912 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -381,3 +381,48 @@ explain (costs off)
                      Index Cond: (ff = '42'::bigint)
 (14 rows)
 
+-- test equivalence filters
+explain (costs off)
+  select * from ec0
+  inner join ec1 on ec0.ff = ec1.ff
+  where ec0.ff between 1 and 10;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Merge Join
+   Merge Cond: (ec0.ff = ec1.ff)
+   ->  Sort
+         Sort Key: ec0.ff
+         ->  Bitmap Heap Scan on ec0
+               Recheck Cond: ((ff >= 1) AND (ff <= 10))
+               ->  Bitmap Index Scan on ec0_pkey
+                     Index Cond: ((ff >= 1) AND (ff <= 10))
+   ->  Sort
+         Sort Key: ec1.ff
+         ->  Bitmap Heap Scan on ec1
+               Recheck Cond: ((ff >= 1) AND (ff <= 10))
+               ->  Bitmap Index Scan on ec1_pkey
+                     Index Cond: ((ff >= 1) AND (ff <= 10))
+(14 rows)
+
+explain (costs off)
+  select * from ec0
+  inner join ec1 on ec0.ff = ec1.ff
+  where ec1.ff between 1 and 10;
+                         QUERY PLAN                         
+------------------------------------------------------------
+ Merge Join
+   Merge Cond: (ec0.ff = ec1.ff)
+   ->  Sort
+         Sort Key: ec0.ff
+         ->  Bitmap Heap Scan on ec0
+               Recheck Cond: ((ff >= 1) AND (ff <= 10))
+               ->  Bitmap Index Scan on ec0_pkey
+                     Index Cond: ((ff >= 1) AND (ff <= 10))
+   ->  Sort
+         Sort Key: ec1.ff
+         ->  Bitmap Heap Scan on ec1
+               Recheck Cond: ((ff >= 1) AND (ff <= 10))
+               ->  Bitmap Index Scan on ec1_pkey
+                     Index Cond: ((ff >= 1) AND (ff <= 10))
+(14 rows)
+
diff --git a/src/test/regress/sql/equivclass.sql b/src/test/regress/sql/equivclass.sql
index 17fad67..bd5c5b3 100644
--- a/src/test/regress/sql/equivclass.sql
+++ b/src/test/regress/sql/equivclass.sql
@@ -222,3 +222,14 @@ explain (costs off)
      union all
      select ff + 4 as x from ec1) as ss1
   where ss1.x = ec1.f1 and ec1.ff = 42::int8;
+
+-- test equivalence filters
+explain (costs off)
+  select * from ec0
+  inner join ec1 on ec0.ff = ec1.ff
+  where ec0.ff between 1 and 10;
+
+explain (costs off)
+  select * from ec0
+  inner join ec1 on ec0.ff = ec1.ff
+  where ec1.ff between 1 and 10;
