diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index c6b60fa57982eb126c3b3ca948addd12b7a3b22a..a6945d35658a0d51bdc464a3d589696447d6d5b2 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -341,6 +341,21 @@ GetForeignJoinPaths (PlannerInfo *root,
      See <xref linkend="fdw-planning"> for additional information.
     </para>
 
+    <para>
+<programlisting>
+void
+GetExistingLocalJoinPath(RelOptInfo *joinrel)
+</programlisting>
+     The function returns copy of a local join path, which can be converted
+     into an alternative local join plan, which may be useful when
+     implementing a <literal>RecheckForeignScan</> method.  The function
+     searches for a parallel-safe, unparameterized path in the
+     <literal>pathlist</> of given <literal>joinrel</>. If it does not find
+     such a path, it returns NULL, in which case a foreign data wrapper may
+     build the local path by itself or may choose not to create access paths
+     for that join.
+    </para>
+
    </sect2>
 
    <sect2 id="fdw-callbacks-update">
@@ -794,6 +809,9 @@ RecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot);
      can be executed and the resulting tuple can be stored in the slot.
      This plan need not be efficient since no base table will return more
      than one row; for example, it may implement all joins as nested loops.
+     <literal>GetExistingLocalJoinPath</> may be used to search existing paths
+     for a suitable local join path, which can be used as the alternative
+     local join plan.
     </para>
    </sect2>
 
@@ -1069,6 +1087,20 @@ GetForeignTable(Oid relid);
 
     <para>
 <programlisting>
+UserMapping *
+GetUserMappingById(Oid umid);
+</programlisting>
+
+     This function returns the <structname>UserMapping</structname> object for
+     the given user mapping OID.  The OID of a user mapping for a foreign scan
+     is available in the <structname>RelOptInfo</structname>.
+     If there is no mapping for the OID, this function will throw an error.
+     A <structname>UserMapping</structname> object contains properties of the
+     user mapping (see <filename>foreign/foreign.h</filename> for details).
+    </para>
+
+    <para>
+<programlisting>
 List *
 GetForeignColumnOptions(Oid relid, AttrNumber attnum);
 </programlisting>
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 47c00af74f9c68e96bfc50aee8b03246441448a8..213217966cfb648de09af413916302ca55829143 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -160,6 +160,54 @@ GetForeignServerByName(const char *srvname, bool missing_ok)
 	return GetForeignServer(serverid);
 }
 
+/*
+ * GetUserMappingById - look up the user mapping by its OID.
+ */
+UserMapping *
+GetUserMappingById(Oid umid)
+{
+	Datum		datum;
+	HeapTuple	tp;
+	bool		isnull;
+	UserMapping *um;
+
+	tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umid));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for user mapping %u", umid);
+
+	um = (UserMapping *) palloc(sizeof(UserMapping));
+	um->umid = umid;
+
+	/* Extract the umuser */
+	datum = SysCacheGetAttr(USERMAPPINGOID,
+							tp,
+							Anum_pg_user_mapping_umuser,
+							&isnull);
+	Assert(!isnull);
+	um->userid = DatumGetObjectId(datum);
+
+	/* Extract the umserver */
+	datum = SysCacheGetAttr(USERMAPPINGOID,
+							tp,
+							Anum_pg_user_mapping_umserver,
+							&isnull);
+	Assert(!isnull);
+	um->serverid = DatumGetObjectId(datum);
+
+	/* Extract the umoptions */
+	datum = SysCacheGetAttr(USERMAPPINGOID,
+							tp,
+							Anum_pg_user_mapping_umoptions,
+							&isnull);
+	if (isnull)
+		um->options = NIL;
+	else
+		um->options = untransformRelOptions(datum);
+
+	ReleaseSysCache(tp);
+
+	return um;
+}
 
 /*
  * GetUserMapping - look up the user mapping.
@@ -240,8 +288,8 @@ find_user_mapping(Oid userid, Oid serverid)
 
 	/* Not found for the specific user -- try PUBLIC */
 	tp = SearchSysCache2(USERMAPPINGUSERSERVER,
-							 ObjectIdGetDatum(InvalidOid),
-							 ObjectIdGetDatum(serverid));
+						 ObjectIdGetDatum(InvalidOid),
+						 ObjectIdGetDatum(serverid));
 
 	if (!HeapTupleIsValid(tp))
 		ereport(ERROR,
@@ -732,3 +780,115 @@ get_foreign_server_oid(const char *servername, bool missing_ok)
 				 errmsg("server \"%s\" does not exist", servername)));
 	return oid;
 }
+
+/*
+ * Get a copy of an existing local path for a given join relation.
+ *
+ * This function is usually helpful to obtain an alternate local path for EPQ
+ * checks.
+ *
+ * Right now, this function only supports unparameterized foreign joins, so we
+ * only search for unparameterized path in the given list of paths. Since we
+ * are searching for a path which can be used to construct an alternative local
+ * plan for a foreign join, we look for only MergeJoin, HashJoin or NestLoop
+ * paths.
+ *
+ * If the inner or outer subpath of the chosen path is a ForeignScan, we
+ * replace it with its outer subpath.  For this reason, and also because the
+ * planner might free the original path later, the path returned by this
+ * function is a shallow copy of the original.  There's no need to copy
+ * the substructure, so we don't.
+ *
+ * Since the plan created using this path will presumably only be used to
+ * execute EPQ checks, efficiency of the path is not a concern. But since the
+ * list passed is expected to be from RelOptInfo, it's anyway sorted by total
+ * cost and hence we are likely to choose the most efficient path, which is
+ * all for the best.
+ */
+extern Path *
+GetExistingLocalJoinPath(RelOptInfo *joinrel)
+{
+	ListCell   *lc;
+
+	Assert(joinrel->reloptkind == RELOPT_JOINREL);
+
+	foreach(lc, joinrel->pathlist)
+	{
+		Path	   *path = (Path *) lfirst(lc);
+		JoinPath   *joinpath = NULL;
+
+		/* Skip parameterised or non-parallel-safe paths. */
+		if (path->param_info != NULL || !path->parallel_safe)
+			continue;
+
+		switch (path->pathtype)
+		{
+			case T_HashJoin:
+				{
+					HashPath   *hash_path = makeNode(HashPath);
+
+					memcpy(hash_path, path, sizeof(HashPath));
+					joinpath = (JoinPath *) hash_path;
+				}
+				break;
+
+			case T_NestLoop:
+				{
+					NestPath   *nest_path = makeNode(NestPath);
+
+					memcpy(nest_path, path, sizeof(NestPath));
+					joinpath = (JoinPath *) nest_path;
+				}
+				break;
+
+			case T_MergeJoin:
+				{
+					MergePath  *merge_path = makeNode(MergePath);
+
+					memcpy(merge_path, path, sizeof(MergePath));
+					joinpath = (JoinPath *) merge_path;
+				}
+				break;
+
+			default:
+
+				/*
+				 * Just skip anything else. We don't know if corresponding
+				 * plan would build the output row from whole-row references
+				 * of base relations and execute the EPQ checks.
+				 */
+				break;
+		}
+
+		/* This path isn't good for us, check next. */
+		if (!joinpath)
+			continue;
+
+		/*
+		 * If either inner or outer path is a ForeignPath corresponding to a
+		 * pushed down join, replace it with the fdw_outerpath, so that we
+		 * maintain path for EPQ checks built entirely of local join
+		 * strategies.
+		 */
+		if (IsA(joinpath->outerjoinpath, ForeignPath))
+		{
+			ForeignPath *foreign_path;
+
+			foreign_path = (ForeignPath *) joinpath->outerjoinpath;
+			if (foreign_path->path.parent->reloptkind == RELOPT_JOINREL)
+				joinpath->outerjoinpath = foreign_path->fdw_outerpath;
+		}
+
+		if (IsA(joinpath->innerjoinpath, ForeignPath))
+		{
+			ForeignPath *foreign_path;
+
+			foreign_path = (ForeignPath *) joinpath->innerjoinpath;
+			if (foreign_path->path.parent->reloptkind == RELOPT_JOINREL)
+				joinpath->innerjoinpath = foreign_path->fdw_outerpath;
+		}
+
+		return (Path *) joinpath;
+	}
+	return NULL;
+}
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index e16fbf34ec858354df07e48d133c1337f471e0a9..9fafab06e956f637bd7f8d4245254d714d850d80 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -202,5 +202,6 @@ extern FdwRoutine *GetFdwRoutineByRelId(Oid relid);
 extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy);
 extern bool IsImportableForeignTable(const char *tablename,
 						 ImportForeignSchemaStmt *stmt);
+extern Path *GetExistingLocalJoinPath(RelOptInfo *joinrel);
 
 #endif   /* FDWAPI_H */
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index d1359163e48eb5b17993258f34fa7fa1cf6555a6..71f8e55b0e95911aa44bb07d14f5c334d081084f 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -73,6 +73,7 @@ extern ForeignServer *GetForeignServer(Oid serverid);
 extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
 extern UserMapping *GetUserMapping(Oid userid, Oid serverid);
 extern Oid GetUserMappingId(Oid userid, Oid serverid);
+extern UserMapping *GetUserMappingById(Oid umid);
 extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
 extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
 							bool missing_ok);