diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index c00fa30..d30d715 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -3732,611 +3732,248 @@ get_parameterized_joinrel_size(PlannerInfo *root, RelOptInfo *rel,
 }
 
 /*
- * clause_is_fk_compatible
- *		Verify that the clause may be compatible with foreign keys (i.e. it's
- * 		a simple operator clause with two Vars, referencing two different
- * 		relations).
+ * find_best_match_foreign_key
+ *		Returns a Bitmapset with bits set for each 0 based index of 'joinquals'
+ *		which to a foreign key which is defined on fkrel.
  *
- * This only checks form of the clause (and returns 'true' if it may match
- * a foreign key), but does not cross-check the clause against existing foreign
- * keys for example.
+ * Note: in the event that there's more than 1 foreign key between these two
+ * relations, the largest match, i.e. the bitmask for the one with the most
+ * matching 'joinquals' items, is returned.
  *
- * It also extracts data from the clause - rangetable indexes and attnums for
- * each Var (if the function returns 'false' then those values are undefined
- * and may contain garbage).
+ * XXX it's possible to trick this code when the joinquals contain duplicate
+ * quals. Is this worth fixing?
  */
-static bool
-clause_is_fk_compatible(RestrictInfo *rinfo,
-						Index *varnoa, AttrNumber *attnoa,
-						Index *varnob, AttrNumber *attnob,
-						Oid *opno)
+static Bitmapset *
+find_best_match_foreign_key(PlannerInfo *root, RelOptInfo *fkrel,
+							RelOptInfo *foreignrel, List *joinquals)
 {
-	OpExpr *clause;
-	Var	   *var0, *var1;
-
-	/* We only expect restriction clauses here, with operator expression. */
-
-	if (! IsA(rinfo, RestrictInfo))
-		return false;
-
-	if (! IsA(rinfo->clause, OpExpr))
-		return false;
+	Bitmapset	   *bestmatch;
+	Bitmapset	   *usefulquals;
+	ListCell	   *lc;
+	int				lstidx;
+	Oid				frelid;
 
-	clause = (OpExpr*)rinfo->clause;
-
-	/* The clause has to use exactly two simple variables. */
-
-	if (list_length(clause->args) != 2)
-		return false;
-
-	var0 = list_nth(clause->args, 0);
-	var1 = list_nth(clause->args, 1);
-
-	if (! (IsA(var0, Var) && IsA(var1, Var)))
-		return false;
+	/* fast path out when there's no foreign keys on fkrel */
+	if (fkrel->fkeylist == NIL)
+		return NULL;
 
-	/* The variables has to reference two different rangetable entries. */
-
-	if (var0->varno == var1->varno)
-		return false;
+	bestmatch = NULL;
+	lstidx = -1;
+	usefulquals = NULL;
 
 	/*
-	 * At this point we know the clause has the right structure, so extract
-	 * the interesting info we'll need outside. We don't really track which
-	 * relation is inner/outer, so we'll check both directions.
+	 * First build a bitmapset with all possibly useful quals. This will save
+	 * from having to do this for each foreign key later. The only quals that
+	 * are useful to us are in the form "var op var", so here we'll ignore
+	 * things like "function(var1, var2)".
 	 */
+	foreach(lc, joinquals)
+	{
+		RestrictInfo   *rinfo = (RestrictInfo *) lfirst(lc);
+		OpExpr		   *clause;
+		Var			   *leftvar, *rightvar;
 
-	*varnoa = var0->varno;
-	*attnoa = var0->varattno;
-
-	*varnob = var1->varno;
-	*attnob = var1->varattno;
+		/*
+		 * Increment this at the start of the loop to save doing it before each
+		 * continue statement.
+		 */
+		lstidx++;
 
-	*opno = clause->opno;
+		if (!IsA(rinfo, RestrictInfo))
+			continue;
 
-	return true;
-}
+		if (!IsA(rinfo->clause, OpExpr))
+			continue;
 
+		clause = (OpExpr *) rinfo->clause;
 
-/*
- * fkey_is_matched_by_clauses
- * 		Check whether the foreign key is "fully" matched by the clauses.
- *
- * This checks whether the foreign key is fully matched by clauses, i.e. if
- * there's a clause matching perfectly all the parts of the foreign key.
- */
-static bool
-fkey_is_matched_by_clauses(PlannerInfo *root, ForeignKeyOptInfo *fkinfo,
-						   List *clauses, int relid, int frelid)
-{
-	int			i;
-	ListCell   *lc;
-	bool		r;
-	bool	   *matched;
+		if (list_length(clause->args) != 2)
+			continue;
 
-	/* we should only get multi-column foreign keys here */
-	Assert(fkinfo->nkeys > 1);
+		leftvar = (Var *) get_leftop((Expr *) clause);
+		rightvar = (Var *) get_rightop((Expr *) clause);
 
-	/* flags for each part of the foreign key */
-	matched = palloc0(fkinfo->nkeys);
+		/* Foreign keys only support Vars, so ignore anything more complex */
+		if (!IsA(leftvar, Var) || !IsA(rightvar, Var))
+			continue;
 
-	foreach (lc, clauses)
-	{
-		RestrictInfo   *rinfo = (RestrictInfo*)lfirst(lc);
+		usefulquals = bms_add_member(usefulquals, lstidx);
+	}
 
-		/* info extracted from the clause (opno, varnos and varattnos) */
-		Oid				opno;
-		Index			relida, relidb;
-		AttrNumber		attnoa, attnob;
+	frelid = root->simple_rte_array[foreignrel->relid]->relid;
 
-		/* we'll look this up in the simple_rte_array table */
-		Oid				oida, oidb;
+	/* now check the matches for each foreign key defined on the fkrel */
+	foreach(lc, fkrel->fkeylist)
+	{
+		ForeignKeyOptInfo *fkinfo = (ForeignKeyOptInfo *) lfirst(lc);
+		Bitmapset *matches;
+		int i;
 
-		/*
-		 * If the clause has structure incompatible with foreign keys (not an
-		 * operator clause with two Var nodes), just skip it.
-		 */
-		if (! clause_is_fk_compatible(rinfo, &relida, &attnoa,
-											 &relidb, &attnob, &opno))
+		/* skip any foreign keys which don't reference frelid */
+		if (fkinfo->confrelid != frelid)
 			continue;
 
-		/* lookup range table entries for the indexes */
-		oida = root->simple_rte_array[relida]->relid;
-		oidb = root->simple_rte_array[relidb]->relid;
+		matches = NULL;
 
 		/*
-		 * Check if the clause matches any part of the foreign key.
+		 * Loop over each column of the foreign key and build a bitmap index
+		 * of each joinqual which matches. Note that we don't stop when we find
+		 * the first match, as the expression could be duplicated in the
+		 * joinquals, and we want to match as many as possible.
 		 */
 		for (i = 0; i < fkinfo->nkeys; i++)
 		{
-			/* if the operator does not match, try next key */
-			if (! fkinfo->conpfeqop[i] == opno)
-				continue;
-
-			/*
-			 * We don't know in what order the clause lists the Vars, so we'll check
-			 * the foreign key in both directions (it does not really matter).
-			 */
-			if ((oida == fkinfo->conrelid) && (oidb == fkinfo->confrelid))
-			{
-				if ((fkinfo->confkeys[i] == attnob) &&
-					(fkinfo->conkeys[i] == attnoa))
-					matched[i] = true;
-			}
-
-			if ((oida == fkinfo->confrelid) && (oidb == fkinfo->conrelid))
-			{
-				if ((fkinfo->confkeys[i] == attnoa) &&
-					(fkinfo->conkeys[i] == attnob))
-						matched[i] = true;
-			}
-		}
-	}
-
-	/* return 'true' if all the parts of the foreign key were matched */
-	r = true;
-	for (i = 0; i < fkinfo->nkeys; i++)
-		r &= matched[i];
-
-	pfree(matched);
-
-	return r;
-}
-
-/*
- * find_satisfied_fkeys
- * 		Searches for all foreign keys fully-satisfied by the join clauses.
- *
- * A join is fully-satisfied if all the parts are matched by at least one
- * join condition (same operator and attnos).
- *
- * This returns a list of foreign keys of identified foreign keys (or NIL),
- * and also selectivity for all the (matched) foreign keys.
- */
-static List *
-find_satisfied_fkeys(PlannerInfo *root, SpecialJoinInfo *sjinfo, List *joinquals,
-					 Selectivity *sel)
-{
-	int	inner, outer;
-	List *fkeys = NIL;
-
-	/*
-	 * We'll take all combinations of inner/outer relations, and check if
-	 * there are foreign keys between them. If we found a foreign key for
-	 * the pair of base relations, we'll try matching it to clauses.
-	 *
-	 * We don't know in which direction the foreign keys are created, so
-	 * we'll check both directions (it's allways between inner and outer
-	 * side of the join).
-	 */
-
-	inner = -1;
-	while ((inner = bms_next_member(sjinfo->min_righthand, inner)) >= 0)
-	{
-		RelOptInfo *rel_inner = find_base_rel(root, inner);
-		RangeTblEntry *rt_inner = planner_rt_fetch(inner, root);
-
-		Assert(rel_inner->reloptkind == RELOPT_BASEREL);
-
-		outer = -1;
-		while ((outer = bms_next_member(sjinfo->min_lefthand, outer)) >= 0)
-		{
-			ListCell   *lc;
-			RelOptInfo *rel_outer = find_base_rel(root, outer);
-			RangeTblEntry *rt_outer = planner_rt_fetch(outer, root);
-
-			Assert(rel_outer->reloptkind == RELOPT_BASEREL);
+			ListCell *lc2;
 
-			/*
-			 * Walk through foreign keys defined on the inner side, referencing
-			 * relation on the outer side.
-			 */
-			foreach (lc, rel_inner->fkeylist)
+			lstidx = -1;
+			foreach(lc2, joinquals)
 			{
-				ForeignKeyOptInfo  *fkinfo = (ForeignKeyOptInfo *)lfirst(lc);
+				RestrictInfo   *rinfo;
+				OpExpr		   *clause;
+				Var			   *leftvar;
+				Var			   *rightvar;
 
-				/* We only care about keys referencing the current outer relation */
-				if (fkinfo->confrelid != rt_outer->relid)
-					continue;
+				lstidx++;
 
-				/*
-				 * And we don't care about foreign keys with less than two columns
-				 * (those clauses will be handled by the regular estimation, unless
-				 * matched by some other key).
-				 *
-				 * XXX Maybe we should estimate even the single-column keys here,
-				 *     as it's really cheap. But it can't do any cross-table check
-				 *     of MCV lists or whatever clauselist_selectivity() does.
-				 */
-				if (fkinfo->nkeys < 2)
+				/* skip anything we didn't mark as useful above. */
+				if (!bms_is_member(lstidx, usefulquals))
 					continue;
 
 				/*
-				 * Finally check if the foreign key is full matched by clauses,
-				 * and update the selectivity (simply use 1/cardinality of the
-				 * table referenced by the foreign key).
-				 *
-				 * XXX Notice we're using 'rel->tuples' here and not 'rows',
-				 *     because we need the cardinality (before applying clauses).
+				 * Here we can safely assume that we have an OpExpr, in  the
+				 * from of "var op var"
 				 */
-				if (fkey_is_matched_by_clauses(root, fkinfo, joinquals, inner, outer))
-				{
-					fkeys = lappend(fkeys, fkinfo);
-					*sel *= (1.0 / rel_outer->tuples);
-				}
-
-			}
-
-			/*
-			 * And now check foreign keys in the other direction (defined on
-			 * outer relation, referencing inner).
-			 *
-			 * XXX This does exactly the same thing as the previous loop, so no
-			 *     comments.
-			 *
-			 * TODO Merge those two blocks into a single utility function to
-			 *      reduce the code duplication.
-			 */
-			foreach (lc, rel_outer->fkeylist)
-			{
-				ForeignKeyOptInfo  *fkinfo = (ForeignKeyOptInfo *)lfirst(lc);
+				rinfo = (RestrictInfo *) lfirst(lc2);
+				clause = (OpExpr *) rinfo->clause;
 
-				if (fkinfo->confrelid != rt_inner->relid)
+				/* skip if the operator does not match */
+				if (clause->opno != fkinfo->conpfeqop[i])
 					continue;
 
-				if (fkinfo->nkeys < 2)
-					continue;
+				leftvar = (Var *) get_leftop((Expr *) clause);
+				rightvar = (Var *) get_rightop((Expr *) clause);
 
-				if (fkey_is_matched_by_clauses(root, fkinfo, joinquals, outer, inner))
-				{
-					fkeys = lappend(fkeys, fkinfo);
-					*sel *= (1.0 / rel_inner->tuples);
-				}
+				/*
+				 * Check if the OpExpr matches the foreign key. Remember that
+				 * this could be written with the Vars in either order, so we
+				 * test both permutations of the expression.
+				 */
+				if (fkinfo->confkeys[i] == leftvar->varattno &&
+					fkinfo->conkeys[i] == rightvar->varattno)
+					matches = bms_add_member(matches, lstidx);
 
+				else if (fkinfo->confkeys[i] == rightvar->varattno &&
+					fkinfo->conkeys[i] == leftvar->varattno)
+					matches = bms_add_member(matches, lstidx);
 			}
+		}
 
+		/* Is this match better than the current best match? */
+		if (bms_num_members(matches) > bms_num_members(bestmatch))
+		{
+			bms_free(bestmatch);
+			bestmatch = matches;
 		}
 	}
 
-	return fkeys;
+	return bestmatch;
 }
 
 /*
- * filter_fk_join_clauses
- *		Remove the clauses that were used to match foreign keys (and will be
- * 		estimated using the selectivity from  keys).
- *
- * Once we identify the foreign keys matched by clauses, we need to remove the
- * clauses so that we don't include them into the estimate twice. This method
- * performs that - cross-checks the foreign keys and clauses and removes all
- * clauses matching any of the foreign keys.
- *
- * If there are no foreign keys, this simply returns the original list of
- * clauses. Otherwise it builds a new list (without modifying the source one).
+ * clauselist_join_selectivity
+ *		Estimate selectivity of join clauses either by using foreign key info
+ *		or by using the regular clauselist_selectivity().
+ *
+ * Since selectivity estimates for each joinqual are multiplied together, this
+ * can cause significant underestimates on the number of join tuples in cases
+ * where there's more than 1 clause in the join condition. To help ease the
+ * pain here we make use of foreign keys, and we assume that 1 row will match
+ * when *all* of the foreign key columns are present in the join condition, any
+ * additional clauses are estimated using clauselist_selectivity().
+ *
+ * XXX is it worth adding code to do something smart when we only get a partial
+ * foreign key match?
  */
-static List *
-filter_fk_join_clauses(PlannerInfo *root, SpecialJoinInfo *sjinfo, List *fkeys,
-					   List *joinquals)
+static Selectivity
+clauselist_join_selectivity(PlannerInfo *root, List *joinquals,
+							JoinType jointype, SpecialJoinInfo *sjinfo)
 {
-	ListCell   *lc,
-			   *lc2;
-	List	   *clauses = NIL;
-
-	/* if there are no foreign keys, return the original list */
-	if (list_length(fkeys) == 0)
-		return joinquals;
+	int outerrelid;
+	int innerrelid;
 
-	foreach (lc, joinquals)
+	/*
+	 * Since foreign keys can only references a single relation, and each
+	 * foreign key belongs to at most a single relation, we require that only
+	 * a single relation be at either side of the join condition. We'll fall
+	 * back on clauselist_selectivity() if this is not the case.
+	 */
+	if (bms_get_singleton_member(sjinfo->min_righthand, &innerrelid) &&
+		bms_get_singleton_member(sjinfo->min_lefthand, &outerrelid))
 	{
-		Oid				opno;
-		Index			relida, relidb;
-		AttrNumber		attnoa, attnob;
-		Oid				oida, oidb;
-
-		/* was the clause matched by at least one key? */
-		bool			matched = false;
-
-		RestrictInfo   *rinfo = (RestrictInfo*)lfirst(lc);
+		Bitmapset *outer2inner, *inner2outer;
+		RelOptInfo *innerrel = find_base_rel(root, innerrelid);
+		RelOptInfo *outerrel = find_base_rel(root, outerrelid);
 
-		/* if the clause is not compatible with foreign keys, just add it */
-		if (! clause_is_fk_compatible(rinfo, &relida, &attnoa,
-											 &relidb, &attnob, &opno))
-		{
-			clauses = lappend(clauses, rinfo);
-			continue;
-		}
+		/* check which quals are matched by a foreign key referencing the innerrel */
+		outer2inner = find_best_match_foreign_key(root, outerrel, innerrel, joinquals);
 
-		oida = root->simple_rte_array[relida]->relid;
-		oidb = root->simple_rte_array[relidb]->relid;
+		/* do the same again, but with relations swapped */
+		inner2outer = find_best_match_foreign_key(root, innerrel, outerrel, joinquals);
 
 		/*
-		 * Walk through the matched foreign keys, and try to match the clause
-		 * against each one. We don't know in what order are the Vars listed
-		 * in the clause, so try both ways.
+		 * did we find any matches at all? If so we need to see which one is
+		 * the best/longest match
 		 */
-		foreach (lc2, fkeys)
+		if (outer2inner || inner2outer)
 		{
-			int i;
-			ForeignKeyOptInfo *fkinfo = (ForeignKeyOptInfo*)lfirst(lc2);
+			Selectivity sel;
+			ListCell *lc;
+			int lstidx;
+			List *nonfkeyclauses;
 
-			/* now check the keys - we can stop after finding the first match */
-			for (i = 0; i < fkinfo->nkeys; i++)
+			/* either could be NULL, but bms_num_members will handle that */
+			if (bms_num_members(outer2inner) < bms_num_members(inner2outer))
 			{
-				/* check the operator first */
-				if (fkinfo->conpfeqop[i] != opno)
-					continue;
+				/*
+				 * We can now disgard the lesser of the two and save the best
+				 * match into one of the bitmaps. Let's just use outer2inner
+				 */
+				bms_free(outer2inner);
+				outer2inner = inner2outer;
 
-				/* now check the attnums */
-				if ((fkinfo->conrelid == oida) && (fkinfo->confrelid == oidb))
-				{
-					if ((fkinfo->confkeys[i] == attnob) &&
-						(fkinfo->conkeys[i] == attnoa))
-					{
-						matched = true;
-						break;
-					}
-				}
-				else if ((fkinfo->conrelid == oidb) && (fkinfo->confrelid == oida))
-				{
-					if ((fkinfo->confkeys[i] == attnoa) &&
-						(fkinfo->conkeys[i] == attnob))
-					{
-						matched = true;
-						break;
-					}
-				}
+				if (jointype == JOIN_SEMI || jointype == JOIN_ANTI)
+					sel = 1.0;
+				else
+					sel = 1.0 / Max(outerrel->tuples, 1.0);
 			}
+			else
+			{
+				bms_free(inner2outer);
 
-			/* no need to try more keys, single match is enough */
-			if (matched)
-				break;
-		}
-
-		/* if a clause was not matched by any foreign key, continue */
-		if (! matched)
-			clauses = lappend(clauses, rinfo);
-
-	}
-
-	return clauses;
-}
-
-
-
-/*
- * Estimate selectivity of join clauses - either by using foreign key info or
- * by using the regular clauselist_selectivity().
- *
- * If there are multiple join clauses, we check whether the clauses match
- * a foreign key between the tables - in that case we can use this information
- * to derive a better estimate (otherwise we'd multiply the selectivities for
- * each clause, which often causes significant underestimates).
- *
- * We only need to care about multi-clause join conditions and simply defer
- * simple clauses to clauselist_selectivity().
- *
- * Let's see a few examples of foreign-key joins, illustrating the estimation
- * ideas here.
- *
- * CREATE TABLE a (
- *     a1 INT,
- *     a2 INT,
- *     a3 INT,
- *     a4 INT,
- *     PRIMARY KEY (a1, a2, a3)
- * );
- *
- * CREATE TABLE b (
- *     b1 INT,
- *     b2 INT,
- *     b3 INT,
- *     b4 INT,
- *     FOREIGN KEY (b1, b2, b3) REFERENCES (a1, a2, a3)
- * );
- *
- * clauses exactly match a foreign key
- * -----------------------------------
- *
- *   SELECT * FROM a JOIN b ON (a1=b1 AND a2=b2 AND a3=b3);
- *
- * - trivial, just use 1/card(a)
- *
- * clauses match a foreign key, with additional conditions exist
- * -------------------------------------------------------------
- *
- *   SELECT * FROM a JOIN b ON (a1=b1 AND a2=b2 AND a3=b3 AND a4=b4);
- *
- * - trivial, just use 1/card(a) * selectivity(remaining_clauses)
- *
- * incomplete foreign key match
- * ----------------------------
- *
- *   SELECT * FROM a JOIN b ON (a1=b1 AND a2=b2);
- *
- * - not sure, we'd need to compensate for the "missing part" somehow (we know
- *   the row exists, but we don't know much many rows - it's likely more than
- *   unique)
- *
- * - one way would be to assume each clause is responsible for (1/card(a))^(1/n)
- *   where 'n' is number of clauses - this way 'multiplying the FK clauses' would
- *   gets us the 1/card(a) selectivity if we had all the clauses
- *
- * - another thing is we might use 1/card(a) as a lower boundary - we can't
- *   possibly get lower selectivity, we know the rows exist (also this is not
- *   based on assumptions like the previous idea)
- *
- * multiple distinct foreign keys matching
- * ---------------------------------------
- *
- * CREATE TABLE a (
- *     a1 INT,
- *     a2 INT,
- *     a3 INT,
- *     a4 INT,
- *     PRIMARY KEY (a1, a2),
- *     UNIQUE (a3, a4)
- * );
- *
- * CREATE TABLE b (
- *     b1 INT,
- *     b2 INT,
- *     b3 INT,
- *     b4 INT,
- *     FOREIGN KEY (b1, b2) REFERENCES (a1, a2),
- *     FOREIGN KEY (b3, b4) REFERENCES (a3, a4)
- * );
- *
- * - simply just use 1/card(a) for each foreign key (assumes independence of the
- *   foreign keys, but well - we're assuming attribute independence so this is
- *   an improvement)
- *
- * multiple overlapping foreign keys matching
- * ------------------------------------------
- *
- * CREATE TABLE a (
- *     a1 INT,
- *     a2 INT,
- *     a3 INT,
- *     PRIMARY KEY (a1, a2),
- *     UNIQUE (a2, a3)
- * );
- *
- * CREATE TABLE b (
- *     b1 INT,
- *     b2 INT,
- *     b3 INT,
- *     b4 INT,
- *     FOREIGN KEY (b1, b2) REFERENCES (a1, a2),
- *     FOREIGN KEY (b3, b4) REFERENCES (a2, a3)
- * );
- *
- * - probably just use 1/card(a) for each foreign key, as in the previous
- *   example (assumes independence of the foreign keys, but well - we're
- *   assuming attribute independence so this is an improvement)
- *
- * There are strange cases with multiple foreign keys, where one FK implies
- * the other FK. For example consider this:
- *
- * CREATE TABLE a (
- *     a1 INT,
- *     a2 INT,
- *     a3 INT,
- *     UNIQUE (a1, a2, a3),
- *     UNIQUE (a1, a2)
- * );
- *
- * CREATE TABLE b (
- *     b1 INT,
- *     b2 INT,
- *     b3 INT,
- *     FOREIGN KEY (b1, b2, b3) REFERENCES a (a1, a2, a3),
- *     FOREIGN KEY (b1, b2) REFERENCES a (a1, a2)
- * );
- *
- * Clearly the (b1,b2) is implied by (b1,b2,b3) - if the latter exists, then
- * the former exists too. Not sure how to handle this (or if it's actually
- * needed).
- *
- * Another slightly strange case is FK constraints in both directions (these
- * statements don't work - the foreign keys need to be established using
- * ALTER, but for illustration it's sufficient).
- *
- * CREATE TABLE a (
- *     a1 INT,
- *     a2 INT,
- *     UNIQUE (a1, a2),
- *     FOREIGN KEY (a1, a2) REFERENCES a (b1, b2)
- * );
- *
- * CREATE TABLE b (
- *     b1 INT,
- *     b2 INT,
- *     UNIQUE (b1, b2),
- *     FOREIGN KEY (b1, b2) REFERENCES a (a1, a2)
- * );
- *
- * which effectively establishes 1:1 relationship, or with distinct groups of
- * columns for each direction
- *
- * CREATE TABLE a (
- *     a1 INT,
- *     a2 INT,
- *     a3 INT,
- *     a4 INT,
- *     UNIQUE (a1, a2),
- *     FOREIGN KEY (a3, a4) REFERENCES a (b1, b2)
- * );
- *
- * CREATE TABLE b (
- *     b1 INT,
- *     b2 INT,
- *     b3 INT,
- *     b4 INT,
- *     UNIQUE (b1, b2),
- *     FOREIGN KEY (b3, b4) REFERENCES a (a1, a2)
- * );
- *
- * which creates a cycle of foreign keys.
- *
- * In the first case the foreign keys should be counted only once into the
- * selectivity, because it's effectively a 1:1 relationship - each row has
- * to have one matching row in the other table (not matched by other) rows.
- *
- * In the other case, it's probably right to factor in both foreign keys.
- */
-static Selectivity
-clauselist_join_selectivity(PlannerInfo *root, List *joinquals, int varno,
-							JoinType jointype, SpecialJoinInfo *sjinfo)
-{
-	Selectivity sel = 1.0;
-
-	List	   *fkeys;		/* list of satisfied foreign keys */
-	List	   *unmatched;	/* clauses remaining after removing FK clauses */
-
-	Assert(list_length(joinquals) >= 0);
-
-	/*
-	 * If we only have a single clause, we don't need to mess with the foreign
-	 * keys - that's only useful with multi-column clauses anyway. Just use the
-	 * simple clauselist_selectivity.
-	 */
-	if (list_length(joinquals) <= 1)
-		return clauselist_selectivity(root,
-									  joinquals,
-									  0,
-									  jointype,
-									  sjinfo);
-
-	/*
-	 * First we'll identify foreign keys that are fully matched by the join
-	 * clauses, and we'll update the selectivity accordingly while doing so.
-	 */
-	fkeys = find_satisfied_fkeys(root, sjinfo, joinquals, &sel);
-
-	/*
-	 * Now that we have the foreign keys, we can get rid of the clauses
-	 * matching any of them, and only keep the remaining clauses, so that
-	 * we can estimate them using the regular selectivity estimation.
-	 */
-	unmatched = filter_fk_join_clauses(root, sjinfo, fkeys, joinquals);
+				if (jointype == JOIN_SEMI || jointype == JOIN_ANTI)
+					sel = 1.0;
+				else
+					sel = 1.0 / Max(innerrel->tuples, 1.0);
+			}
 
-	/*
-	 * Estimate the remaining clauses (not matching any FK), if still have any.
-	 */
-	if (list_length(unmatched) > 0)
-	{
-		sel *= clauselist_selectivity(root,
-									  unmatched,
-									  0,
-									  jointype,
-									  sjinfo);
-
-		/* Only free the list if we actually found any foreign keys. */
-		if (list_length(fkeys) > 0)
-			list_free(unmatched);
+			/*
+			 * build a list of all non-fkey joinquals, we'll need to enlist
+			 * clauselist_selectivity() to estimate these
+			 */
+			lstidx = 0;
+			nonfkeyclauses = NIL;
+			foreach (lc, joinquals)
+			{
+				if (!bms_is_member(lstidx, outer2inner))
+					nonfkeyclauses = lappend(nonfkeyclauses, lfirst(lc));
+				lstidx++;
+			}
+			return sel * clauselist_selectivity(root, nonfkeyclauses, 0, jointype, sjinfo);
+		}
 	}
 
-	return sel;
+	/* perform normal estimation without the help of foreign keys */
+	return clauselist_selectivity(root, joinquals, 0, jointype, sjinfo);
 }
 
 /*
@@ -4387,7 +4024,6 @@ calc_joinrel_size_estimate(PlannerInfo *root,
 		/* Get the separate selectivities */
 		jselec = clauselist_join_selectivity(root,
 											 joinquals,
-											 0,
 											 jointype,
 											 sjinfo);
 
@@ -4405,7 +4041,6 @@ calc_joinrel_size_estimate(PlannerInfo *root,
 	{
 		jselec = clauselist_join_selectivity(root,
 											 restrictlist,
-											 0,
 											 jointype,
 											 sjinfo);
 		pselec = 0.0;			/* not used, keep compiler quiet */
diff --git a/src/backend/optimizer/plan/analyzejoins.c b/src/backend/optimizer/plan/analyzejoins.c
index 05c8698..7912b15 100644
--- a/src/backend/optimizer/plan/analyzejoins.c
+++ b/src/backend/optimizer/plan/analyzejoins.c
@@ -562,6 +562,7 @@ remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved)
 	return result;
 }
 
+
 /*
  * query_supports_distinctness - could the query possibly be proven distinct
  *		on some set of output columns?
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index fbc5579..dbe6038 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -96,6 +96,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	bool		hasindex;
 	List	   *indexinfos = NIL;
 	List	   *fkinfos = NIL;
+	List	   *fkoidlist;
+	ListCell   *l;
 
 	/*
 	 * We need not lock the relation since it was already locked, either by
@@ -143,7 +145,6 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	if (hasindex)
 	{
 		List	   *indexoidlist;
-		ListCell   *l;
 		LOCKMODE	lmode;
 
 		indexoidlist = RelationGetIndexList(relation);
@@ -384,86 +385,75 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 	rel->indexlist = indexinfos;
  
-	/*
-	 * TODO Can we do something like (hasindex) here? Is it necessary? The
-	 *      trouble with that is that we don't have a good place to reset that
-	 *      flag (relhasindex is reset by vacuum, but is has nothing to do with
-	 *      foreign keys at this point).
-	 */
-	if (true)
-	{
-		List	   *fkoidlist;
-		ListCell   *l;
-
-		fkoidlist = RelationGetFKeyList(relation);
-
-		foreach(l, fkoidlist)
-		{
-			int			i;
-			ArrayType  *arr;
-			Datum		adatum;
-			bool		isnull;
-			int			numkeys;
-			Oid			fkoid = lfirst_oid(l);
+	/* load foreign keys */
+	fkoidlist = RelationGetFKeyList(relation);
 
-			HeapTuple	htup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fkoid));
-			Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
+	foreach(l, fkoidlist)
+	{
+		int			i;
+		ArrayType  *arr;
+		Datum		adatum;
+		bool		isnull;
+		int			numkeys;
+		Oid			fkoid = lfirst_oid(l);
 
-			ForeignKeyOptInfo *info;
+		HeapTuple	htup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fkoid));
+		Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
 
-			Assert(constraint->contype == CONSTRAINT_FOREIGN);
+		ForeignKeyOptInfo *info;
 
-			info = makeNode(ForeignKeyOptInfo);
+		Assert(constraint->contype == CONSTRAINT_FOREIGN);
 
-			info->conrelid = constraint->conrelid;
-			info->confrelid = constraint->confrelid;
+		info = makeNode(ForeignKeyOptInfo);
 
-			/* conkey */
-			adatum = SysCacheGetAttr(CONSTROID, htup,
-									 Anum_pg_constraint_conkey, &isnull);
-			Assert(!isnull);
+		info->conrelid = constraint->conrelid;
+		info->confrelid = constraint->confrelid;
 
-			arr = DatumGetArrayTypeP(adatum);
-			numkeys = ARR_DIMS(arr)[0];
-			info->conkeys = (int*)palloc0(numkeys * sizeof(int));
+		/* conkey */
+		adatum = SysCacheGetAttr(CONSTROID, htup,
+									Anum_pg_constraint_conkey, &isnull);
+		Assert(!isnull);
 
-			for (i = 0; i < numkeys; i++)
-				info->conkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
+		arr = DatumGetArrayTypeP(adatum);
+		numkeys = ARR_DIMS(arr)[0];
+		info->conkeys = (int*)palloc0(numkeys * sizeof(int));
 
-			/* confkey */
-			adatum = SysCacheGetAttr(CONSTROID, htup,
-									 Anum_pg_constraint_confkey, &isnull);
-			Assert(!isnull);
+		for (i = 0; i < numkeys; i++)
+			info->conkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
 
-			arr = DatumGetArrayTypeP(adatum);
-			numkeys = ARR_DIMS(arr)[0];
-			info->confkeys = (int*)palloc0(numkeys * sizeof(int));
+		/* confkey */
+		adatum = SysCacheGetAttr(CONSTROID, htup,
+									Anum_pg_constraint_confkey, &isnull);
+		Assert(!isnull);
 
-			for (i = 0; i < numkeys; i++)
-				info->confkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
+		arr = DatumGetArrayTypeP(adatum);
+		numkeys = ARR_DIMS(arr)[0];
+		info->confkeys = (int*)palloc0(numkeys * sizeof(int));
 
-			/* conpfeqop */
-			adatum = SysCacheGetAttr(CONSTROID, htup,
-									 Anum_pg_constraint_conpfeqop, &isnull);
-			Assert(!isnull);
+		for (i = 0; i < numkeys; i++)
+			info->confkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
 
-			arr = DatumGetArrayTypeP(adatum);
-			numkeys = ARR_DIMS(arr)[0];
-			info->conpfeqop = (Oid*)palloc0(numkeys * sizeof(Oid));
+		/* conpfeqop */
+		adatum = SysCacheGetAttr(CONSTROID, htup,
+									Anum_pg_constraint_conpfeqop, &isnull);
+		Assert(!isnull);
 
-			for (i = 0; i < numkeys; i++)
-				info->conpfeqop[i] = ((Oid *) ARR_DATA_PTR(arr))[i];
+		arr = DatumGetArrayTypeP(adatum);
+		numkeys = ARR_DIMS(arr)[0];
+		info->conpfeqop = (Oid*)palloc0(numkeys * sizeof(Oid));
 
-			info->nkeys = numkeys;
+		for (i = 0; i < numkeys; i++)
+			info->conpfeqop[i] = ((Oid *) ARR_DATA_PTR(arr))[i];
 
-			ReleaseSysCache(htup);
+		info->nkeys = numkeys;
 
-			fkinfos = lcons(info, fkinfos);
-		}
+		ReleaseSysCache(htup);
 
-		list_free(fkoidlist);
+		fkinfos = lcons(info, fkinfos);
 	}
 
+	list_free(fkoidlist);
+
 	rel->fkeylist = fkinfos;
 
 	/* Grab foreign-table info using the relcache, while we have it */
