diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 4658e59..7e17f8e 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -143,6 +143,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
 	pstmt->relationOids = NIL;
 	pstmt->invalItems = NIL;	/* workers can't replan anyway... */
 	pstmt->hasRowSecurity = false;
+	pstmt->hasForeignJoin = false;
 
 	/* Return serialized copy of our dummy PlannedStmt. */
 	return nodeToString(pstmt);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 953aa62..86d9fcf 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2149,6 +2149,10 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 	/* Likewise, copy the relids that are represented by this foreign scan */
 	scan_plan->fs_relids = best_path->path.parent->relids;
 
+	/* If a join between foreign relations was pushed down, remember it */
+	if (scan_relid == 0)
+		root->glob->hasForeignJoin = true;
+
 	/*
 	 * Replace any outer-relation variables with nestloop params in the qual,
 	 * fdw_exprs and fdw_recheck_quals expressions.  We do this last so that
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 131dc8a..6414b2f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -200,6 +200,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	glob->lastPlanNodeId = 0;
 	glob->transientPlan = false;
 	glob->hasRowSecurity = false;
+	glob->hasForeignJoin = false;
 
 	/*
 	 * Assess whether it's feasible to use parallel mode for this query. We
@@ -346,6 +347,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	result->nParamExec = glob->nParamExec;
 	result->hasRowSecurity = glob->hasRowSecurity;
 	result->parallelModeNeeded = glob->parallelModeNeeded;
+	result->hasForeignJoin = glob->hasForeignJoin;
 
 	return result;
 }
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 539f4b9..10876d9 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -610,6 +610,18 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
 		plansource->is_valid = false;
 
 	/*
+	 * If we have a join pushed down to the foreign server and the current user
+	 * is different from the one for which the plan was created, invalidate the
+	 * generic plan since user mapping for the new user might make the join
+	 * unsafe to push or may be pushed with differen user mapping. Since this
+	 * does not change the query tree, there is not need to invalidate the
+	 * entire plansource.
+	 */
+	if (plansource->hasForeignJoin && plansource->planUserId != GetUserId() &&
+			plansource->gplan)
+		plansource->gplan->is_valid = false;
+
+	/*
 	 * If the query is currently valid, acquire locks on the referenced
 	 * objects; then check again.  We need to do it this way to cover the race
 	 * condition that an invalidation message arrives before we get the locks.
@@ -881,6 +893,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	bool		spi_pushed;
 	MemoryContext plan_context;
 	MemoryContext oldcxt = CurrentMemoryContext;
+	ListCell	*lc;
 
 	/*
 	 * Normally the querytree should be valid already, but if it's not,
@@ -937,6 +950,20 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	 */
 	plist = pg_plan_queries(qlist, plansource->cursor_options, boundParams);
 
+	/*
+	 * Walk through the plist and set hasForeignJoin if any of the plans have it
+	 * set.
+	 */
+	plansource->hasForeignJoin = false;
+	foreach(lc, plist)
+	{
+		PlannedStmt	*plan_stmt = (PlannedStmt *)lfirst(lc);
+
+		if (IsA(plan_stmt, PlannedStmt))
+			plansource->hasForeignJoin = plansource->hasForeignJoin ||
+										 plan_stmt->hasForeignJoin;
+	}
+
 	/* Clean up SPI state */
 	SPI_pop_conditional(spi_pushed);
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index c92579b..e52b960 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -73,6 +73,7 @@ typedef struct PlannedStmt
 	bool		hasRowSecurity; /* row security applied? */
 
 	bool		parallelModeNeeded; /* parallel mode required to execute? */
+	bool		hasForeignJoin;	/* Plan has a pushed down foreign join */
 } PlannedStmt;
 
 /* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 518352c..4616b94 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -108,6 +108,7 @@ typedef struct PlannerGlobal
 	bool		parallelModeOK; /* parallel mode potentially OK? */
 
 	bool		parallelModeNeeded;		/* parallel mode actually required? */
+	bool		hasForeignJoin;	/* does have a pushed down foreign join */
 } PlannerGlobal;
 
 /* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index 0929f58..e07fece 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -111,6 +111,7 @@ typedef struct CachedPlanSource
 	int			num_custom_plans;		/* number of plans included in total */
 	bool		hasRowSecurity; /* planned with row security? */
 	bool		row_security_env;		/* row security setting when planned */
+	Oid			hasForeignJoin;	/* Plan has a pushed down foreign join */
 } CachedPlanSource;
 
 /*
