diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c2f0e0f8eee4be39ddbfec4f670620f74e0ee347..389808c9e9553b0ed1735546d4d96597f748a496 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2137,16 +2137,6 @@ _outIndexOptInfo(StringInfo str, const IndexOptInfo *node)
 	/* we don't bother with fields copied from the index AM's API struct */
 }
 
-static void
-_outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node)
-{
-	WRITE_NODE_TYPE("FOREIGNKEYOPTINFO");
-
-	WRITE_OID_FIELD(conrelid);
-	WRITE_OID_FIELD(confrelid);
-	WRITE_INT_FIELD(nkeys);
-}
-
 static void
 _outEquivalenceClass(StringInfo str, const EquivalenceClass *node)
 {
@@ -3617,9 +3607,6 @@ outNode(StringInfo str, const void *obj)
 			case T_IndexOptInfo:
 				_outIndexOptInfo(str, obj);
 				break;
-			case T_ForeignKeyOptInfo:
-				_outForeignKeyOptInfo(str, obj);
-				break;
 			case T_EquivalenceClass:
 				_outEquivalenceClass(str, obj);
 				break;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 4c9d8d9cbd5770c3e9970fde3aff18eb0bf36afd..b39c928eba167c3d3541aba5600a736fbf75c79a 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -126,7 +126,6 @@ bool		enable_nestloop = true;
 bool		enable_material = true;
 bool		enable_mergejoin = true;
 bool		enable_hashjoin = true;
-bool		enable_fkey_estimates = true;
 
 typedef struct
 {
@@ -3888,361 +3887,6 @@ get_parameterized_joinrel_size(PlannerInfo *root, RelOptInfo *rel,
 	return nrows;
 }
 
-/*
- * quals_match_foreign_key
- *		Determines if the foreign key is matched by joinquals.
- *
- * Checks that there are conditions on all columns of the foreign key, matching
- * the operator used by the foreign key etc. If such complete match is found,
- * the function returns bitmap identifying the matching quals (0-based).
- *
- * Otherwise (no match at all or incomplete match), NULL is returned.
- *
- * XXX It seems possible in the future to do something useful when a
- * partial match occurs between join and FK, but that is less common
- * and that part isn't worked out yet.
- */
-static Bitmapset *
-quals_match_foreign_key(PlannerInfo *root, ForeignKeyOptInfo *fkinfo,
-						RelOptInfo *fkrel, RelOptInfo *foreignrel,
-						List *joinquals)
-{
-	int i;
-	int nkeys = fkinfo->nkeys;
-	Bitmapset *qualmatches = NULL;
-	Bitmapset *fkmatches = NULL;
-
-	/*
-	 * Loop over each column of the foreign key and build a bitmapset
-	 * 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 generate a bitmapset which has bits set for
-	 * every matching join qual.
-	 */
-	for (i = 0; i < nkeys; i++)
-	{
-		ListCell *lc;
-		int quallstidx = -1;
-
-		foreach(lc, joinquals)
-		{
-			RestrictInfo   *rinfo;
-			OpExpr		   *clause;
-			Var			   *leftvar;
-			Var			   *rightvar;
-
-			quallstidx++;
-
-			/*
-			 * Technically we don't need to, but here we skip this qual if
-			 * we've matched it to part of the foreign key already. This
-			 * should prove to be a useful optimization when the quals appear
-			 * in the same order as the foreign key's keys. We need only bother
-			 * doing this when the foreign key is made up of more than 1 set
-			 * of columns, and we're not testing the first column.
-			 */
-			if (i > 0 && bms_is_member(quallstidx, qualmatches))
-				continue;
-
-			rinfo = (RestrictInfo *) lfirst(lc);
-			clause = (OpExpr *) rinfo->clause;
-
-			/* only OpExprs are useful for consideration */
-			if (!IsA(clause, OpExpr))
-				continue;
-
-			/*
-			 * If the operator does not match then there's little point in
-			 * checking the operands.
-			 */
-			if (clause->opno != fkinfo->conpfeqop[i])
-				continue;
-
-			leftvar = (Var *) get_leftop((Expr *) clause);
-			rightvar = (Var *) get_rightop((Expr *) clause);
-
-			/* Foreign keys only support Vars, so ignore anything more complex */
-			if (!IsA(leftvar, Var) || !IsA(rightvar, Var))
-				continue;
-
-			/*
-			 * For RestrictInfos built from an eclass we must consider each
-			 * member of the eclass as rinfo's operands may not belong to the
-			 * foreign key. For efficient tracking of which Vars we've found,
-			 * since we're only tracking 2 Vars, we use a bitmask. We can
-			 * safely finish searching when both of the least significant bits
-			 * are set.
-			 */
-			if (rinfo->parent_ec)
-			{
-				EquivalenceClass   *ec = rinfo->parent_ec;
-				ListCell		   *lc2;
-				int					foundvarmask = 0;
-
-				foreach(lc2, ec->ec_members)
-				{
-					EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
-					Var *var = (Var *) em->em_expr;
-
-					if (!IsA(var, Var))
-						continue;
-
-					if (foreignrel->relid == var->varno &&
-						fkinfo->confkeys[i] == var->varattno)
-						foundvarmask |= 1;
-
-					else if (fkrel->relid == var->varno &&
-						fkinfo->conkeys[i] == var->varattno)
-						foundvarmask |= 2;
-
-					/*
-					 * Check if we've found both matches. If found we add
-					 * this qual to the matched list and mark this key as
-					 * matched too.
-					 */
-					if (foundvarmask == 3)
-					{
-						qualmatches = bms_add_member(qualmatches, quallstidx);
-						fkmatches = bms_add_member(fkmatches, i);
-						break;
-					}
-				}
-			}
-			else
-			{
-				/*
-				 * In this non eclass RestrictInfo case we'll check if the left
-				 * and right Vars match to this part of the foreign key.
-				 * Remember that this could be written with the Vars in either
-				 * order, so we test both permutations of the expression.
-				 */
-				if ((foreignrel->relid == leftvar->varno) &&
-					(fkrel->relid == rightvar->varno) &&
-					(fkinfo->confkeys[i] == leftvar->varattno) &&
-					(fkinfo->conkeys[i] == rightvar->varattno))
-				{
-					qualmatches = bms_add_member(qualmatches, quallstidx);
-					fkmatches = bms_add_member(fkmatches, i);
-				}
-				else if ((foreignrel->relid == rightvar->varno) &&
-						 (fkrel->relid == leftvar->varno) &&
-						 (fkinfo->confkeys[i] == rightvar->varattno) &&
-						 (fkinfo->conkeys[i] == leftvar->varattno))
-				{
-					qualmatches = bms_add_member(qualmatches, quallstidx);
-					fkmatches = bms_add_member(fkmatches, i);
-				}
-			}
-		}
-	}
-
-	/* can't find more matches than columns in the foreign key */
-	Assert(bms_num_members(fkmatches) <= nkeys);
-
-	/* Only return the matches if the foreign key is matched fully. */
-	if (bms_num_members(fkmatches) == nkeys)
-	{
-		bms_free(fkmatches);
-		return qualmatches;
-	}
-
-	bms_free(fkmatches);
-	bms_free(qualmatches);
-
-	return NULL;
-}
-
-/*
- * find_best_foreign_key_quals
- * 		Finds the foreign key best matching the joinquals.
- *
- * Analyzes joinquals to determine if any quals match foreign keys defined the
- * two relations (fkrel referencing foreignrel). When multiple foreign keys
- * match, we choose the one with the most keys as the best one because of the
- * way estimation occurs in clauselist_join_selectivity().  We could choose
- * the FK matching the most quals, however we assume the quals may be duplicated.
- *
- * We also track which joinquals match the current foreign key, so that we can
- * easily skip then when computing the selectivity.
- *
- * When no matching foreign key is found we return 0, otherwise we return the
- * number of keys in the foreign key.
- *
- * Foreign keys matched only partially are currently ignored.
- */
-static int
-find_best_foreign_key_quals(PlannerInfo *root, RelOptInfo *fkrel,
-							RelOptInfo *foreignrel, List *joinquals,
-							Bitmapset **joinqualsbitmap)
-{
-	Bitmapset	   *qualbestmatch;
-	ListCell	   *lc;
-	int				bestmatchnkeys;
-
-	/*
-	 * fast path out when there's no foreign keys on fkrel, or when use of
-	 * foreign keys for estimation is disabled by GUC
-	 */
-	if ((fkrel->fkeylist == NIL) || (!enable_fkey_estimates))
-	{
-		*joinqualsbitmap = NULL;
-		return 0;
-	}
-
-	qualbestmatch = NULL;
-	bestmatchnkeys = 0;
-
-	/* now check the matches for each foreign key defined on the fkrel */
-	foreach(lc, fkrel->fkeylist)
-	{
-		ForeignKeyOptInfo *fkinfo = (ForeignKeyOptInfo *) lfirst(lc);
-		Bitmapset *qualsmatched;
-
-		/*
-		 * We make no attempt in checking that this foreign key actually
-		 * references 'foreignrel', the reasoning here is that we may be able
-		 * to match the foreign key to an eclass member Var of a RestrictInfo
-		 * that's in qualslist, this Var may belong to some other relation.
-		 *
-		 * XXX Is this assumption safe in all cases? Maybe not, but does
-		 * it lead to a worse estimate than the previous approach? Doubt it.
-		 */
-		qualsmatched = quals_match_foreign_key(root, fkinfo, fkrel, foreignrel,
-											   joinquals);
-
-		/* Did we get a match? And is that match better than a previous one? */
-		if (qualsmatched != NULL && fkinfo->nkeys > bestmatchnkeys)
-		{
-			/* save the new best match */
-			bms_free(qualbestmatch);
-			qualbestmatch = qualsmatched;
-			bestmatchnkeys = fkinfo->nkeys;
-		}
-	}
-
-	*joinqualsbitmap = qualbestmatch;
-	return bestmatchnkeys;
-}
-
-/*
- * 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().
- *
- * Note this ignores whether the FK is invalid or currently deferred; we don't
- * rely on this assumption for correctness of the query, so it is a reasonable
- * and safe assumption for planning purposes.
- */
-static Selectivity
-clauselist_join_selectivity(PlannerInfo *root, List *joinquals,
-							JoinType jointype, SpecialJoinInfo *sjinfo)
-{
-	int				outerid;
-	int				innerid;
-	Selectivity		sel = 1.0;
-	Bitmapset	   *foundfkquals = NULL;
-
-	innerid = -1;
-	while ((innerid = bms_next_member(sjinfo->min_righthand, innerid)) >= 0)
-	{
-		RelOptInfo *innerrel = find_base_rel(root, innerid);
-
-		outerid = -1;
-		while ((outerid = bms_next_member(sjinfo->min_lefthand, outerid)) >= 0)
-		{
-			RelOptInfo	   *outerrel = find_base_rel(root, outerid);
-			Bitmapset	   *outer2inner;
-			Bitmapset	   *inner2outer;
-			int				innermatches;
-			int				outermatches;
-
-			/*
-			 * check which quals are matched by a foreign key referencing the
-			 * innerrel.
-			 */
-			outermatches = find_best_foreign_key_quals(root, outerrel,
-											innerrel, joinquals, &outer2inner);
-
-			/* do the same, but with relations swapped */
-			innermatches = find_best_foreign_key_quals(root, innerrel,
-											outerrel, joinquals, &inner2outer);
-
-			/*
-			 * did we find any matches at all? If so we need to see which one is
-			 * the best/longest match
-			 */
-			if (outermatches != 0 || innermatches != 0)
-			{
-				double	referenced_tuples;
-				bool overlap;
-
-				/* either could be zero, but not both. */
-				if (outermatches < innermatches)
-				{
-					overlap = bms_overlap(foundfkquals, inner2outer);
-
-					foundfkquals = bms_add_members(foundfkquals, inner2outer);
-					referenced_tuples = Max(outerrel->tuples, 1.0);
-				}
-				else
-				{
-					overlap = bms_overlap(foundfkquals, outer2inner);
-
-					foundfkquals = bms_add_members(foundfkquals, outer2inner);
-					referenced_tuples = Max(innerrel->tuples, 1.0);
-				}
-
-				/*
-				 * XXX should we ignore these overlapping matches?
-				 * Or perhaps take the Max() or Min()?
-				 */
-				if (overlap)
-				{
-					if (jointype == JOIN_SEMI || jointype == JOIN_ANTI)
-						sel = Min(sel,Min(1.0 / (outerrel->tuples / Max(innerrel->tuples, 1.0)), 1.0));
-					else
-						sel = Min(sel, 1.0 / referenced_tuples);
-				}
-				else
-				{
-					if (jointype == JOIN_SEMI || jointype == JOIN_ANTI)
-						sel *= Min(1.0 / (outerrel->tuples / Max(innerrel->tuples, 1.0)), 1.0);
-					else
-						sel *= 1.0 / referenced_tuples;
-				}
-			}
-		}
-	}
-
-	/*
-	 * If any non matched quals exist then we build a list of the non-matches
-	 * and use clauselist_selectivity() to estimate the selectivity of these.
-	 */
-	if (bms_num_members(foundfkquals) < list_length(joinquals))
-	{
-		ListCell *lc;
-		int lstidx = 0;
-		List *nonfkeyclauses = NIL;
-
-		foreach (lc, joinquals)
-		{
-			if (!bms_is_member(lstidx, foundfkquals))
-				nonfkeyclauses = lappend(nonfkeyclauses, lfirst(lc));
-			lstidx++;
-		}
-		sel *= clauselist_selectivity(root, nonfkeyclauses, 0, jointype, sjinfo);
-	}
-
-	return sel;
-}
-
 /*
  * calc_joinrel_size_estimate
  *		Workhorse for set_joinrel_size_estimates and
@@ -4289,11 +3933,11 @@ calc_joinrel_size_estimate(PlannerInfo *root,
 		}
 
 		/* Get the separate selectivities */
-		jselec = clauselist_join_selectivity(root,
-											 joinquals,
-											 jointype,
-											 sjinfo);
-
+		jselec = clauselist_selectivity(root,
+										joinquals,
+										0,
+										jointype,
+										sjinfo);
 		pselec = clauselist_selectivity(root,
 										pushedquals,
 										0,
@@ -4306,10 +3950,11 @@ calc_joinrel_size_estimate(PlannerInfo *root,
 	}
 	else
 	{
-		jselec = clauselist_join_selectivity(root,
-											 restrictlist,
-											 jointype,
-											 sjinfo);
+		jselec = clauselist_selectivity(root,
+										restrictlist,
+										0,
+										jointype,
+										sjinfo);
 		pselec = 0.0;			/* not used, keep compiler quiet */
 	}
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index b0b606ebc82380306dbbefc22b6aa7ee3526ebf6..3a585ade747722791b1fda1b06e0d4b14c8c2e17 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -28,7 +28,6 @@
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/pg_am.h"
-#include "catalog/pg_constraint.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -42,7 +41,6 @@
 #include "rewrite/rewriteManip.h"
 #include "storage/bufmgr.h"
 #include "utils/lsyscache.h"
-#include "utils/syscache.h"
 #include "utils/rel.h"
 #include "utils/snapmgr.h"
 
@@ -96,9 +94,6 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	Relation	relation;
 	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
@@ -149,6 +144,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 	if (hasindex)
 	{
 		List	   *indexoidlist;
+		ListCell   *l;
 		LOCKMODE	lmode;
 
 		indexoidlist = RelationGetIndexList(relation);
@@ -395,85 +391,6 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 	rel->indexlist = indexinfos;
 
-	/*
-	 * Load foreign key data. Note this is the definitional data from the
-	 * catalog only and does not lock the referenced tables here. The
-	 * precise definition of the FK is important and may affect the usage
-	 * elsewhere in the planner, e.g. if the constraint is deferred or
-	 * if the constraint is not valid then relying upon this in the executor
-	 * may not be accurate, though might be considered a useful estimate for
-	 * planning purposes.
-	 */
-	fkoidlist = RelationGetFKeyList(relation);
-
-	foreach(l, fkoidlist)
-	{
-		Oid			fkoid = lfirst_oid(l);
-		HeapTuple	htup;
-		Form_pg_constraint constraint;
-		ForeignKeyOptInfo *info;
-		Datum		adatum;
-		bool		isnull;
-		ArrayType  *arr;
-		int			numkeys;
-		int			i;
-
-		htup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fkoid));
-		if (!HeapTupleIsValid(htup)) /* should not happen */
-			elog(ERROR, "cache lookup failed for constraint %u", fkoid);
-		constraint = (Form_pg_constraint) GETSTRUCT(htup);
-
-		Assert(constraint->contype == CONSTRAINT_FOREIGN);
-
-		info = makeNode(ForeignKeyOptInfo);
-
-		info->conrelid = constraint->conrelid;
-		info->confrelid = constraint->confrelid;
-
-		/* conkey */
-		adatum = SysCacheGetAttr(CONSTROID, htup,
-									Anum_pg_constraint_conkey, &isnull);
-		Assert(!isnull);
-
-		arr = DatumGetArrayTypeP(adatum);
-		numkeys = ARR_DIMS(arr)[0];
-		info->conkeys = (int*)palloc(numkeys * sizeof(int));
-		for (i = 0; i < numkeys; i++)
-			info->conkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
-
-		/* confkey */
-		adatum = SysCacheGetAttr(CONSTROID, htup,
-									Anum_pg_constraint_confkey, &isnull);
-		Assert(!isnull);
-
-		arr = DatumGetArrayTypeP(adatum);
-		Assert(numkeys == ARR_DIMS(arr)[0]);
-		info->confkeys = (int*)palloc(numkeys * sizeof(int));
-		for (i = 0; i < numkeys; i++)
-			info->confkeys[i] = ((int16 *) ARR_DATA_PTR(arr))[i];
-
-		/* conpfeqop */
-		adatum = SysCacheGetAttr(CONSTROID, htup,
-									Anum_pg_constraint_conpfeqop, &isnull);
-		Assert(!isnull);
-
-		arr = DatumGetArrayTypeP(adatum);
-		Assert(numkeys == ARR_DIMS(arr)[0]);
-		info->conpfeqop = (Oid*)palloc(numkeys * sizeof(Oid));
-		for (i = 0; i < numkeys; i++)
-			info->conpfeqop[i] = ((Oid *) ARR_DATA_PTR(arr))[i];
-
-		info->nkeys = numkeys;
-
-		ReleaseSysCache(htup);
-
-		fkinfos = lappend(fkinfos, info);
-	}
-
-	list_free(fkoidlist);
-
-	rel->fkeylist = fkinfos;
-
 	/* Grab foreign-table info using the relcache, while we have it */
 	if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
 	{
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 79cc3df590ae97a8b942e299bb3fc3a8d56bf15a..1b7b99548c5a8c666d65f59d6305ce80f3ee2646 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2031,7 +2031,6 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 			FreeTupleDesc(relation->rd_att);
 	}
 	list_free(relation->rd_indexlist);
-	list_free(relation->rd_fkeylist);
 	bms_free(relation->rd_indexattr);
 	bms_free(relation->rd_keyattr);
 	bms_free(relation->rd_idattr);
@@ -3957,79 +3956,6 @@ RelationGetIndexList(Relation relation)
 	return result;
 }
 
-/*
- * RelationGetFKeyList -- get a list of foreign key oids
- *
- * Use an index scan on pg_constraint to load in FK definitions,
- * intended for use within the planner, not for enforcing FKs.
- *
- * Data is ordered by Oid, though this is not critical at this point
- * since we do not lock the referenced relations.
- */
-List *
-RelationGetFKeyList(Relation relation)
-{
-	Relation	conrel;
-	SysScanDesc conscan;
-	ScanKeyData skey;
-	HeapTuple	htup;
-	List	   *result;
-	List	   *oldlist;
-	MemoryContext oldcxt;
-
-	/* Quick exit if we already computed the list. */
-	if (relation->rd_fkeylist)
-		return list_copy(relation->rd_fkeylist);
-
-	/* Fast path if no FKs... if it doesn't have a trigger, it can't have a FK */
-	if (!relation->rd_rel->relhastriggers)
-		return NIL;
-	/*
-	 * We build the list we intend to return (in the caller's context) while
-	 * doing the scan.  After successfully completing the scan, we copy that
-	 * list into the relcache entry.  This avoids cache-context memory leakage
-	 * if we get some sort of error partway through.
-	 */
-	result = NIL;
-
-	/* Prepare to scan pg_constraint for entries having conrelid = this rel. */
-	ScanKeyInit(&skey,
-				Anum_pg_constraint_conrelid,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(RelationGetRelid(relation)));
-
-	conrel = heap_open(ConstraintRelationId, AccessShareLock);
-	conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
-								 NULL, 1, &skey);
-
-	while (HeapTupleIsValid(htup = systable_getnext(conscan)))
-	{
-		Form_pg_constraint constraint = (Form_pg_constraint) GETSTRUCT(htup);
-
-		/* return only foreign keys */
-		if (constraint->contype != CONSTRAINT_FOREIGN)
-			continue;
-
-		/* Add FK's OID to result list in the proper order */
-		result = insert_ordered_oid(result, HeapTupleGetOid(htup));
-	}
-
-	systable_endscan(conscan);
-
-	heap_close(conrel, AccessShareLock);
-
-	/* Now save a copy of the completed list in the relcache entry. */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-	oldlist = relation->rd_fkeylist;
-	relation->rd_fkeylist = list_copy(result);
-	MemoryContextSwitchTo(oldcxt);
-
-	/* Don't leak the old list, if there is one */
-	list_free(oldlist);
-
-	return result;
-}
-
 /*
  * insert_ordered_oid
  *		Insert a new Oid into a sorted list of Oids, preserving ordering
@@ -4994,7 +4920,6 @@ load_relcache_init_file(bool shared)
 		rel->rd_indexattr = NULL;
 		rel->rd_keyattr = NULL;
 		rel->rd_idattr = NULL;
-		rel->rd_fkeylist = NIL;
 		rel->rd_createSubid = InvalidSubTransactionId;
 		rel->rd_newRelfilenodeSubid = InvalidSubTransactionId;
 		rel->rd_amcache = NULL;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e246a9c2f0b8e78d25a2a6e17c6b530b019630fc..edfa39adf4adf3a6e01d0457fedc8a0a387f95b0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -877,15 +877,6 @@ static struct config_bool ConfigureNamesBool[] =
 		true,
 		NULL, NULL, NULL
 	},
-	{
-		{"enable_fkey_estimates", PGC_USERSET, QUERY_TUNING_METHOD,
-			gettext_noop("Enables use of foreign keys for estimating joins."),
-			NULL
-		},
-		&enable_fkey_estimates,
-		true,
-		NULL, NULL, NULL
-	},
 
 	{
 		{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ea65f2e453ba902cd349b6393dfa3fe131d73a9b..5953db45a89fe7f692d9bc2fcb97f9f9f843f02d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -223,7 +223,6 @@ typedef enum NodeTag
 	T_PlannerGlobal,
 	T_RelOptInfo,
 	T_IndexOptInfo,
-	T_ForeignKeyOptInfo,
 	T_ParamPathInfo,
 	T_Path,
 	T_IndexPath,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 45739c3ee7a3553d5300bb858c652e9fbebce4ec..b1f6cf45f0bdd0582133b325e639127fa075f065 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -516,7 +516,6 @@ typedef struct RelOptInfo
 	List	   *lateral_vars;	/* LATERAL Vars and PHVs referenced by rel */
 	Relids		lateral_referencers;	/* rels that reference me laterally */
 	List	   *indexlist;		/* list of IndexOptInfo */
-	List	   *fkeylist;			/* list of ForeignKeyOptInfo */
 	BlockNumber pages;			/* size estimates derived from pg_class */
 	double		tuples;
 	double		allvisfrac;
@@ -623,27 +622,6 @@ typedef struct IndexOptInfo
 	void		(*amcostestimate) ();	/* AM's cost estimator */
 } IndexOptInfo;
 
-/*
- * ForeignKeyOptInfo
- *		Per-foreign-key information for planning/optimization
- *
- * Only includes columns from pg_constraint related to foreign keys.
- *
- * conkeys[], confkeys[] and conpfeqop[] each have nkeys entries.
- */
-typedef struct ForeignKeyOptInfo
-{
-	NodeTag		type;
-
-	Oid			conrelid;	/* relation constrained by the foreign key */
-	Oid			confrelid;	/* relation referenced by the foreign key */
-
-	int			nkeys;		/* number of columns in the foreign key */
-	int		   *conkeys;	/* attnums of columns in the constrained table */
-	int		   *confkeys;	/* attnums of columns in the referenced table */
-	Oid		   *conpfeqop;	/* OIDs of equality operators used by the FK */
-
-} ForeignKeyOptInfo;
 
 /*
  * EquivalenceClasses
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 58ac1638ec1a27b9161a2effb370ef9b4480c502..d4adca6836a30ead68c5299bd77da66d3ba34129 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -66,7 +66,6 @@ extern bool enable_nestloop;
 extern bool enable_material;
 extern bool enable_mergejoin;
 extern bool enable_hashjoin;
-extern bool enable_fkey_estimates;
 extern int	constraint_exclusion;
 
 extern double clamp_row_est(double nrows);
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index e0ac4515c950252ef66b9dcb905c4ca9a2eca1dc..f3b25e2419c4c1c40d65b32d4e5ef3861ed0ff63 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -76,8 +76,6 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
 							int indexcol,
 							List **indexcolnos,
 							bool *var_on_left_p);
-extern bool has_matching_fkey(RelOptInfo *rel, RelOptInfo *frel, List *clauses,
-							  bool reverse);
 
 /*
  * tidpath.h
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index a0ba4177645e24dc784b729c6e7037d0debcc708..5d354c0f67b33f1a69cd87d13cadaba6c8d2ca85 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -95,9 +95,6 @@ typedef struct RelationData
 	Oid			rd_oidindex;	/* OID of unique index on OID, if any */
 	Oid			rd_replidindex; /* OID of replica identity index, if any */
 
-	/* data managed by RelationGetFKList: */
-	List	   *rd_fkeylist;		/* OIDs of foreign keys */
-
 	/* data managed by RelationGetIndexAttrBitmap: */
 	Bitmapset  *rd_indexattr;	/* identifies columns used in indexes */
 	Bitmapset  *rd_keyattr;		/* cols that can be ref'd by foreign keys */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 7f07c269145b3a76cead7f5efd6755a05d2916c5..1b4830462d52ba0c3fbfbdd341ee0abd741d631c 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -38,7 +38,6 @@ extern void RelationClose(Relation relation);
  * Routines to compute/retrieve additional cached information
  */
 extern List *RelationGetIndexList(Relation relation);
-extern List *RelationGetFKeyList(Relation relation);
 extern Oid	RelationGetOidIndex(Relation relation);
 extern Oid	RelationGetReplicaIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index abe0dc983ee3fac4262c7a8a56762db6a26e02bb..00ef4210549879668afa69344e8b51bccbfb3afb 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1,19 +1,18 @@
 SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
-         name          | setting 
------------------------+---------
- enable_bitmapscan     | on
- enable_fkey_estimates | on
- enable_hashagg        | on
- enable_hashjoin       | on
- enable_indexonlyscan  | on
- enable_indexscan      | on
- enable_material       | on
- enable_mergejoin      | on
- enable_nestloop       | on
- enable_seqscan        | on
- enable_sort           | on
- enable_tidscan        | on
-(12 rows)
+         name         | setting 
+----------------------+---------
+ enable_bitmapscan    | on
+ enable_hashagg       | on
+ enable_hashjoin      | on
+ enable_indexonlyscan | on
+ enable_indexscan     | on
+ enable_material      | on
+ enable_mergejoin     | on
+ enable_nestloop      | on
+ enable_seqscan       | on
+ enable_sort          | on
+ enable_tidscan       | on
+(11 rows)
 
 CREATE TABLE foo2(fooid int, f2 int);
 INSERT INTO foo2 VALUES(1, 11);