*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
***************
*** 128,133 **** static void fileBeginForeignScan(ForeignScanState *node, int eflags);
--- 128,134 ----
  static TupleTableSlot *fileIterateForeignScan(ForeignScanState *node);
  static void fileReScanForeignScan(ForeignScanState *node);
  static void fileEndForeignScan(ForeignScanState *node);
+ static bool fileNeedUserMapping(void);
  static bool fileAnalyzeForeignTable(Relation relation,
  						AcquireSampleRowsFunc *func,
  						BlockNumber *totalpages);
***************
*** 171,176 **** file_fdw_handler(PG_FUNCTION_ARGS)
--- 172,178 ----
  	fdwroutine->IterateForeignScan = fileIterateForeignScan;
  	fdwroutine->ReScanForeignScan = fileReScanForeignScan;
  	fdwroutine->EndForeignScan = fileEndForeignScan;
+ 	fdwroutine->NeedUserMapping = fileNeedUserMapping;
  	fdwroutine->AnalyzeForeignTable = fileAnalyzeForeignTable;
  	fdwroutine->IsForeignScanParallelSafe = fileIsForeignScanParallelSafe;
  
***************
*** 727,732 **** fileEndForeignScan(ForeignScanState *node)
--- 729,744 ----
  }
  
  /*
+  * fileNeedUserMapping
+  *		Test whether any user mapping is needed for scanning a foreign table
+  */
+ static bool
+ fileNeedUserMapping(void)
+ {
+ 	return false;
+ }
+ 
+ /*
   * fileAnalyzeForeignTable
   *		Test whether analyzing this foreign table is supported
   */
*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 2086,2117 **** EXECUTE join_stmt;
   40 |   
  (10 rows)
  
! -- change the session user to view_owner and execute the statement. Because of
! -- change in session user, the plan should get invalidated and created again.
! -- The join will not be pushed down since the joining relations do not have a
! -- valid user mapping.
  SET SESSION ROLE view_owner;
  EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
!                             QUERY PLAN                            
! ------------------------------------------------------------------
!  Limit
!    Output: t1.c1, t2.c1
!    ->  Sort
!          Output: t1.c1, t2.c1
!          Sort Key: t1.c1, t2.c1
!          ->  Hash Left Join
!                Output: t1.c1, t2.c1
!                Hash Cond: (t1.c1 = t2.c1)
!                ->  Foreign Scan on public.ft4 t1
!                      Output: t1.c1, t1.c2, t1.c3
!                      Remote SQL: SELECT c1 FROM "S 1"."T 3"
!                ->  Hash
!                      Output: t2.c1
!                      ->  Foreign Scan on public.ft5 t2
!                            Output: t2.c1
!                            Remote SQL: SELECT c1 FROM "S 1"."T 4"
! (16 rows)
! 
  RESET ROLE;
  DEALLOCATE join_stmt;
  CREATE VIEW v_ft5 AS SELECT * FROM ft5;
--- 2086,2098 ----
   40 |   
  (10 rows)
  
! -- change the session user to view_owner and execute the statement
! -- error thrown due to no user mapping
  SET SESSION ROLE view_owner;
  EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
! ERROR:  user mapping not found for "view_owner"
! EXECUTE join_stmt;
! ERROR:  user mapping not found for "view_owner"
  RESET ROLE;
  DEALLOCATE join_stmt;
  CREATE VIEW v_ft5 AS SELECT * FROM ft5;
***************
*** 2168,2173 **** EXECUTE join_stmt;
--- 2149,2176 ----
  ----+----
  (0 rows)
  
+ -- drop user mapping for view_owner and execute the prepared statement
+ -- both relations use the same user mapping as before, so the join should
+ -- be pushed down
+ DROP USER MAPPING FOR view_owner SERVER loopback;
+ EXPLAIN (COSTS false, VERBOSE) EXECUTE join_stmt;
+                                                                   QUERY PLAN                                                                  
+ ----------------------------------------------------------------------------------------------------------------------------------------------
+  Limit
+    Output: t1.c1, ft5.c1
+    ->  Foreign Scan
+          Output: t1.c1, ft5.c1
+          Relations: (public.ft5 t1) INNER JOIN (public.ft5)
+          Remote SQL: SELECT r1.c1, r6.c1 FROM ("S 1"."T 4" r1 INNER JOIN "S 1"."T 4" r6 ON (((r1.c1 = r6.c1)))) ORDER BY r1.c1 ASC NULLS LAST
+ (6 rows)
+ 
+ EXECUTE join_stmt;
+  c1 | c1 
+ ----+----
+ (0 rows)
+ 
+ -- recreate the dropped user mapping for further tests
+ CREATE USER MAPPING FOR view_owner SERVER loopback;
  -- If a sub-join can't be pushed down, upper level join shouldn't be either.
  EXPLAIN (COSTS false, VERBOSE)
  SELECT t1.c1, t2.c1 FROM (ft5 t1 JOIN v_ft5 t2 ON (t1.c1 = t2.c1)) left join (ft5 t3 JOIN v_ft5 t4 ON (t3.c1 = t4.c1)) ON (t1.c1 = t3.c1);
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
***************
*** 67,74 **** enum FdwScanPrivateIndex
  	FdwScanPrivateRetrievedAttrs,
  	/* Integer representing the desired fetch_size */
  	FdwScanPrivateFetchSize,
- 	/* Oid of user mapping to be used while connecting to the foreign server */
- 	FdwScanPrivateUserMappingOid,
  
  	/*
  	 * String describing join i.e. names of relations being joined and types
--- 67,72 ----
***************
*** 289,294 **** static void postgresBeginForeignScan(ForeignScanState *node, int eflags);
--- 287,293 ----
  static TupleTableSlot *postgresIterateForeignScan(ForeignScanState *node);
  static void postgresReScanForeignScan(ForeignScanState *node);
  static void postgresEndForeignScan(ForeignScanState *node);
+ static bool postgresNeedUserMapping(void);
  static void postgresAddForeignUpdateTargets(Query *parsetree,
  								RangeTblEntry *target_rte,
  								Relation target_relation);
***************
*** 426,431 **** postgres_fdw_handler(PG_FUNCTION_ARGS)
--- 425,431 ----
  	routine->IterateForeignScan = postgresIterateForeignScan;
  	routine->ReScanForeignScan = postgresReScanForeignScan;
  	routine->EndForeignScan = postgresEndForeignScan;
+ 	routine->NeedUserMapping = postgresNeedUserMapping;
  
  	/* Functions for updating foreign tables */
  	routine->AddForeignUpdateTargets = postgresAddForeignUpdateTargets;
***************
*** 1226,1236 **** postgresGetForeignPlan(PlannerInfo *root,
  	 * Build the fdw_private list that will be available to the executor.
  	 * Items in the list must match order in enum FdwScanPrivateIndex.
  	 */
! 	fdw_private = list_make5(makeString(sql.data),
  							 remote_conds,
  							 retrieved_attrs,
! 							 makeInteger(fpinfo->fetch_size),
! 							 makeInteger(foreignrel->umid));
  	if (foreignrel->reloptkind == RELOPT_JOINREL)
  		fdw_private = lappend(fdw_private,
  							  makeString(fpinfo->relation_name->data));
--- 1226,1235 ----
  	 * Build the fdw_private list that will be available to the executor.
  	 * Items in the list must match order in enum FdwScanPrivateIndex.
  	 */
! 	fdw_private = list_make4(makeString(sql.data),
  							 remote_conds,
  							 retrieved_attrs,
! 							 makeInteger(fpinfo->fetch_size));
  	if (foreignrel->reloptkind == RELOPT_JOINREL)
  		fdw_private = lappend(fdw_private,
  							  makeString(fpinfo->relation_name->data));
***************
*** 1278,1318 **** postgresBeginForeignScan(ForeignScanState *node, int eflags)
  	node->fdw_state = (void *) fsstate;
  
  	/*
- 	 * Obtain the foreign server where to connect and user mapping to use for
- 	 * connection. For base relations we obtain this information from
- 	 * catalogs. For join relations, this information is frozen at the time of
- 	 * planning to ensure that the join is safe to pushdown. In case the
- 	 * information goes stale between planning and execution, plan will be
- 	 * invalidated and replanned.
- 	 */
- 	if (fsplan->scan.scanrelid > 0)
- 	{
- 		ForeignTable *table;
- 
- 		/*
- 		 * Identify which user to do the remote access as.  This should match
- 		 * what ExecCheckRTEPerms() does.
- 		 */
- 		RangeTblEntry *rte = rt_fetch(fsplan->scan.scanrelid, estate->es_range_table);
- 		Oid			userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
- 
- 		fsstate->rel = node->ss.ss_currentRelation;
- 		table = GetForeignTable(RelationGetRelid(fsstate->rel));
- 
- 		user = GetUserMapping(userid, table->serverid);
- 	}
- 	else
- 	{
- 		Oid			umid = intVal(list_nth(fsplan->fdw_private, FdwScanPrivateUserMappingOid));
- 
- 		user = GetUserMappingById(umid);
- 		Assert(fsplan->fs_server == user->serverid);
- 	}
- 
- 	/*
  	 * Get connection to the foreign server.  Connection manager will
  	 * establish new connection if necessary.
  	 */
  	fsstate->conn = GetConnection(user, false);
  
  	/* Assign a unique ID for my cursor */
--- 1277,1286 ----
  	node->fdw_state = (void *) fsstate;
  
  	/*
  	 * Get connection to the foreign server.  Connection manager will
  	 * establish new connection if necessary.
  	 */
+ 	user = GetUserMappingById(fsplan->fs_usermapping);
  	fsstate->conn = GetConnection(user, false);
  
  	/* Assign a unique ID for my cursor */
***************
*** 1344,1352 **** postgresBeginForeignScan(ForeignScanState *node, int eflags)
--- 1312,1326 ----
  	 * into local representation and error reporting during that process.
  	 */
  	if (fsplan->scan.scanrelid > 0)
+ 	{
+ 		fsstate->rel = node->ss.ss_currentRelation;
  		fsstate->tupdesc = RelationGetDescr(fsstate->rel);
+ 	}
  	else
+ 	{
+ 		fsstate->rel = NULL;
  		fsstate->tupdesc = node->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
+ 	}
  
  	fsstate->attinmeta = TupleDescGetAttInMetadata(fsstate->tupdesc);
  
***************
*** 1487,1492 **** postgresEndForeignScan(ForeignScanState *node)
--- 1461,1476 ----
  }
  
  /*
+  * postgresNeedUserMapping
+  *		Test whether any user mapping is needed for scanning a foreign table
+  */
+ static bool
+ postgresNeedUserMapping(void)
+ {
+ 	return true;
+ }
+ 
+ /*
   * postgresAddForeignUpdateTargets
   *		Add resjunk column(s) needed for update/delete on a foreign table
   */
***************
*** 2251,2259 **** postgresBeginDirectModify(ForeignScanState *node, int eflags)
  	ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
  	EState	   *estate = node->ss.ps.state;
  	PgFdwDirectModifyState *dmstate;
- 	RangeTblEntry *rte;
- 	Oid			userid;
- 	ForeignTable *table;
  	UserMapping *user;
  	int			numParams;
  
--- 2235,2240 ----
***************
*** 2269,2290 **** postgresBeginDirectModify(ForeignScanState *node, int eflags)
  	dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
  	node->fdw_state = (void *) dmstate;
  
- 	/*
- 	 * Identify which user to do the remote access as.  This should match what
- 	 * ExecCheckRTEPerms() does.
- 	 */
- 	rte = rt_fetch(fsplan->scan.scanrelid, estate->es_range_table);
- 	userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
- 
  	/* Get info about foreign table. */
  	dmstate->rel = node->ss.ss_currentRelation;
- 	table = GetForeignTable(RelationGetRelid(dmstate->rel));
- 	user = GetUserMapping(userid, table->serverid);
  
  	/*
  	 * Get connection to the foreign server.  Connection manager will
  	 * establish new connection if necessary.
  	 */
  	dmstate->conn = GetConnection(user, false);
  
  	/* Initialize state variable */
--- 2250,2263 ----
  	dmstate = (PgFdwDirectModifyState *) palloc0(sizeof(PgFdwDirectModifyState));
  	node->fdw_state = (void *) dmstate;
  
  	/* Get info about foreign table. */
  	dmstate->rel = node->ss.ss_currentRelation;
  
  	/*
  	 * Get connection to the foreign server.  Connection manager will
  	 * establish new connection if necessary.
  	 */
+ 	user = GetUserMappingById(fsplan->fs_usermapping);
  	dmstate->conn = GetConnection(user, false);
  
  	/* Initialize state variable */
***************
*** 3966,3981 **** foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,
  	List	   *otherclauses;
  
  	/*
- 	 * Core code may call GetForeignJoinPaths hook even when the join relation
- 	 * doesn't have a valid user mapping associated with it. See
- 	 * build_join_rel() for details. We can't push down such join, since there
- 	 * doesn't exist a user mapping which can be used to connect to the
- 	 * foreign server.
- 	 */
- 	if (!OidIsValid(joinrel->umid))
- 		return false;
- 
- 	/*
  	 * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins.
  	 * Constructing queries representing SEMI and ANTI joins is hard, hence
  	 * not considered right now.
--- 3939,3944 ----
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 494,505 **** GRANT ALL ON ft5 TO view_owner;
  PREPARE join_stmt AS SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
  EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
  EXECUTE join_stmt;
! -- change the session user to view_owner and execute the statement. Because of
! -- change in session user, the plan should get invalidated and created again.
! -- The join will not be pushed down since the joining relations do not have a
! -- valid user mapping.
  SET SESSION ROLE view_owner;
  EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
  RESET ROLE;
  DEALLOCATE join_stmt;
  
--- 494,504 ----
  PREPARE join_stmt AS SELECT t1.c1, t2.c1 FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10;
  EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
  EXECUTE join_stmt;
! -- change the session user to view_owner and execute the statement
! -- error thrown due to no user mapping
  SET SESSION ROLE view_owner;
  EXPLAIN (COSTS OFF, VERBOSE) EXECUTE join_stmt;
+ EXECUTE join_stmt;
  RESET ROLE;
  DEALLOCATE join_stmt;
  
***************
*** 522,527 **** EXECUTE join_stmt;
--- 521,534 ----
  CREATE USER MAPPING FOR view_owner SERVER loopback;
  EXPLAIN (COSTS false, VERBOSE) EXECUTE join_stmt;
  EXECUTE join_stmt;
+ -- drop user mapping for view_owner and execute the prepared statement
+ -- both relations use the same user mapping as before, so the join should
+ -- be pushed down
+ DROP USER MAPPING FOR view_owner SERVER loopback;
+ EXPLAIN (COSTS false, VERBOSE) EXECUTE join_stmt;
+ EXECUTE join_stmt;
+ -- recreate the dropped user mapping for further tests
+ CREATE USER MAPPING FOR view_owner SERVER loopback;
  
  -- If a sub-join can't be pushed down, upper level join shouldn't be either.
  EXPLAIN (COSTS false, VERBOSE)
*** a/doc/src/sgml/fdwhandler.sgml
--- b/doc/src/sgml/fdwhandler.sgml
***************
*** 283,288 **** EndForeignScan (ForeignScanState *node);
--- 283,300 ----
       to remote servers should be cleaned up.
      </para>
  
+     <para>
+ <programlisting>
+ bool
+ NeedUserMapping (void);
+ </programlisting>
+ 
+      Test whether any user mapping is needed for scanning a foreign table.
+      This is called at the beginning of query planning.  This function takes
+      no arguments, and return <literal>true</> if the FDW needs user mappings
+      and <literal>false</> otherwise.
+     </para>
+ 
     </sect2>
  
     <sect2 id="fdw-callbacks-join-scan">
*** a/src/backend/executor/execParallel.c
--- b/src/backend/executor/execParallel.c
***************
*** 160,166 **** 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);
--- 160,166 ----
  	pstmt->relationOids = NIL;
  	pstmt->invalItems = NIL;	/* workers can't replan anyway... */
  	pstmt->hasRowSecurity = false;
! 	pstmt->usesUserMapping = false;
  
  	/* Return serialized copy of our dummy PlannedStmt. */
  	return nodeToString(pstmt);
*** a/src/backend/foreign/foreign.c
--- b/src/backend/foreign/foreign.c
***************
*** 31,37 ****
  extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
  extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
  
! static HeapTuple find_user_mapping(Oid userid, Oid serverid, bool missing_ok);
  
  /*
   * GetForeignDataWrapper -	look up the foreign-data wrapper by OID.
--- 31,37 ----
  extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
  extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
  
! static HeapTuple find_user_mapping(Oid userid, Oid serverid);
  
  /*
   * GetForeignDataWrapper -	look up the foreign-data wrapper by OID.
***************
*** 223,229 **** GetUserMapping(Oid userid, Oid serverid)
  	bool		isnull;
  	UserMapping *um;
  
! 	tp = find_user_mapping(userid, serverid, false);
  
  	um = (UserMapping *) palloc(sizeof(UserMapping));
  	um->umid = HeapTupleGetOid(tp);
--- 223,229 ----
  	bool		isnull;
  	UserMapping *um;
  
! 	tp = find_user_mapping(userid, serverid);
  
  	um = (UserMapping *) palloc(sizeof(UserMapping));
  	um->umid = HeapTupleGetOid(tp);
***************
*** 250,272 **** GetUserMapping(Oid userid, Oid serverid)
   *
   * If no mapping is found for the supplied user, we also look for
   * PUBLIC mappings (userid == InvalidOid).
-  *
-  * If missing_ok is true, the function returns InvalidOid when it does not find
-  * required user mapping. Otherwise, find_user_mapping() throws error if it
-  * does not find required user mapping.
   */
  Oid
! GetUserMappingId(Oid userid, Oid serverid, bool missing_ok)
  {
  	HeapTuple	tp;
  	Oid			umid;
  
! 	tp = find_user_mapping(userid, serverid, missing_ok);
! 
! 	Assert(missing_ok || tp);
! 
! 	if (!tp && missing_ok)
! 		return InvalidOid;
  
  	/* Extract the Oid */
  	umid = HeapTupleGetOid(tp);
--- 250,263 ----
   *
   * If no mapping is found for the supplied user, we also look for
   * PUBLIC mappings (userid == InvalidOid).
   */
  Oid
! GetUserMappingId(Oid userid, Oid serverid)
  {
  	HeapTuple	tp;
  	Oid			umid;
  
! 	tp = find_user_mapping(userid, serverid);
  
  	/* Extract the Oid */
  	umid = HeapTupleGetOid(tp);
***************
*** 282,294 **** GetUserMappingId(Oid userid, Oid serverid, bool missing_ok)
   *
   * If no mapping is found for the supplied user, we also look for
   * PUBLIC mappings (userid == InvalidOid).
-  *
-  * If missing_ok is true, the function returns NULL, if it does not find
-  * the required user mapping. Otherwise, it throws error if it does not
-  * find the required user mapping.
   */
  static HeapTuple
! find_user_mapping(Oid userid, Oid serverid, bool missing_ok)
  {
  	HeapTuple	tp;
  
--- 273,281 ----
   *
   * If no mapping is found for the supplied user, we also look for
   * PUBLIC mappings (userid == InvalidOid).
   */
  static HeapTuple
! find_user_mapping(Oid userid, Oid serverid)
  {
  	HeapTuple	tp;
  
***************
*** 305,319 **** find_user_mapping(Oid userid, Oid serverid, bool missing_ok)
  						 ObjectIdGetDatum(serverid));
  
  	if (!HeapTupleIsValid(tp))
! 	{
! 		if (missing_ok)
! 			return NULL;
! 		else
! 			ereport(ERROR,
! 					(errcode(ERRCODE_UNDEFINED_OBJECT),
! 					 errmsg("user mapping not found for \"%s\"",
! 							MappingUserName(userid))));
! 	}
  
  	return tp;
  }
--- 292,301 ----
  						 ObjectIdGetDatum(serverid));
  
  	if (!HeapTupleIsValid(tp))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_UNDEFINED_OBJECT),
! 				 errmsg("user mapping not found for \"%s\"",
! 						MappingUserName(userid))));
  
  	return tp;
  }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 97,103 **** _copyPlannedStmt(const PlannedStmt *from)
  	COPY_SCALAR_FIELD(nParamExec);
  	COPY_SCALAR_FIELD(hasRowSecurity);
  	COPY_SCALAR_FIELD(parallelModeNeeded);
! 	COPY_SCALAR_FIELD(hasForeignJoin);
  
  	return newnode;
  }
--- 97,103 ----
  	COPY_SCALAR_FIELD(nParamExec);
  	COPY_SCALAR_FIELD(hasRowSecurity);
  	COPY_SCALAR_FIELD(parallelModeNeeded);
! 	COPY_SCALAR_FIELD(usesUserMapping);
  
  	return newnode;
  }
***************
*** 652,657 **** _copyForeignScan(const ForeignScan *from)
--- 652,658 ----
  	 */
  	COPY_SCALAR_FIELD(operation);
  	COPY_SCALAR_FIELD(fs_server);
+ 	COPY_SCALAR_FIELD(fs_usermapping);
  	COPY_NODE_FIELD(fdw_exprs);
  	COPY_NODE_FIELD(fdw_private);
  	COPY_NODE_FIELD(fdw_scan_tlist);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 273,279 **** _outPlannedStmt(StringInfo str, const PlannedStmt *node)
  	WRITE_INT_FIELD(nParamExec);
  	WRITE_BOOL_FIELD(hasRowSecurity);
  	WRITE_BOOL_FIELD(parallelModeNeeded);
! 	WRITE_BOOL_FIELD(hasForeignJoin);
  }
  
  /*
--- 273,279 ----
  	WRITE_INT_FIELD(nParamExec);
  	WRITE_BOOL_FIELD(hasRowSecurity);
  	WRITE_BOOL_FIELD(parallelModeNeeded);
! 	WRITE_BOOL_FIELD(usesUserMapping);
  }
  
  /*
***************
*** 610,615 **** _outForeignScan(StringInfo str, const ForeignScan *node)
--- 610,616 ----
  
  	WRITE_ENUM_FIELD(operation, CmdType);
  	WRITE_OID_FIELD(fs_server);
+ 	WRITE_OID_FIELD(fs_usermapping);
  	WRITE_NODE_FIELD(fdw_exprs);
  	WRITE_NODE_FIELD(fdw_private);
  	WRITE_NODE_FIELD(fdw_scan_tlist);
***************
*** 2018,2024 **** _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
  	WRITE_BOOL_FIELD(hasRowSecurity);
  	WRITE_BOOL_FIELD(parallelModeOK);
  	WRITE_BOOL_FIELD(parallelModeNeeded);
! 	WRITE_BOOL_FIELD(hasForeignJoin);
  }
  
  static void
--- 2019,2025 ----
  	WRITE_BOOL_FIELD(hasRowSecurity);
  	WRITE_BOOL_FIELD(parallelModeOK);
  	WRITE_BOOL_FIELD(parallelModeNeeded);
! 	WRITE_BOOL_FIELD(usesUserMapping);
  }
  
  static void
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 1402,1408 **** _readPlannedStmt(void)
  	READ_INT_FIELD(nParamExec);
  	READ_BOOL_FIELD(hasRowSecurity);
  	READ_BOOL_FIELD(parallelModeNeeded);
! 	READ_BOOL_FIELD(hasForeignJoin);
  
  	READ_DONE();
  }
--- 1402,1408 ----
  	READ_INT_FIELD(nParamExec);
  	READ_BOOL_FIELD(hasRowSecurity);
  	READ_BOOL_FIELD(parallelModeNeeded);
! 	READ_BOOL_FIELD(usesUserMapping);
  
  	READ_DONE();
  }
***************
*** 1806,1811 **** _readForeignScan(void)
--- 1806,1812 ----
  
  	READ_ENUM_FIELD(operation, CmdType);
  	READ_OID_FIELD(fs_server);
+ 	READ_OID_FIELD(fs_usermapping);
  	READ_NODE_FIELD(fdw_exprs);
  	READ_NODE_FIELD(fdw_private);
  	READ_NODE_FIELD(fdw_scan_tlist);
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 3246,3259 **** 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. The
! 	 * push-down safety of the join depends upon the server and user mapping
! 	 * being same. That can change between planning and execution time, in
! 	 * which case the plan should be invalidated.
! 	 */
! 	if (scan_relid == 0)
! 		root->glob->hasForeignJoin = true;
  
  	/*
  	 * Replace any outer-relation variables with nestloop params in the qual,
--- 3246,3256 ----
  	/* Likewise, copy the relids that are represented by this foreign scan */
  	scan_plan->fs_relids = best_path->path.parent->relids;
  
! 	/* Likewise, copy user mapping OID */
! 	scan_plan->fs_usermapping = rel->umid;
! 
! 	/* Detect whether the FDW uses any user mapping */
! 	root->glob->usesUserMapping = OidIsValid(rel->umid);
  
  	/*
  	 * Replace any outer-relation variables with nestloop params in the qual,
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 220,226 **** 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
--- 220,226 ----
  	glob->lastPlanNodeId = 0;
  	glob->transientPlan = false;
  	glob->hasRowSecurity = false;
! 	glob->usesUserMapping = false;
  
  	/*
  	 * Assess whether it's feasible to use parallel mode for this query. We
***************
*** 417,423 **** 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;
  }
--- 417,423 ----
  	result->nParamExec = glob->nParamExec;
  	result->hasRowSecurity = glob->hasRowSecurity;
  	result->parallelModeNeeded = glob->parallelModeNeeded;
! 	result->usesUserMapping = glob->usesUserMapping;
  
  	return result;
  }
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "miscadmin.h"
  #include "catalog/pg_class.h"
+ #include "foreign/fdwapi.h"
  #include "foreign/foreign.h"
  #include "optimizer/clauses.h"
  #include "optimizer/cost.h"
***************
*** 179,195 **** build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
  		 * Note that if the plan ends up depending on the user OID in any way
  		 * - e.g. if it depends on the computed user mapping OID - we must
  		 * ensure that it gets invalidated in the case of a user OID change.
! 		 * See RevalidateCachedQuery and more generally the hasForeignJoin
  		 * flags in PlannerGlobal and PlannedStmt.
  		 *
  		 * It's possible, and not necessarily an error, for rel->umid to be
  		 * InvalidOid even though rel->serverid is set.  That just means there
  		 * is a server with no user mapping.
  		 */
! 		Oid			userid;
  
! 		userid = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId();
! 		rel->umid = GetUserMappingId(userid, rel->serverid, true);
  	}
  	else
  		rel->umid = InvalidOid;
--- 180,200 ----
  		 * Note that if the plan ends up depending on the user OID in any way
  		 * - e.g. if it depends on the computed user mapping OID - we must
  		 * ensure that it gets invalidated in the case of a user OID change.
! 		 * See RevalidateCachedQuery and more generally the usesUserMapping
  		 * flags in PlannerGlobal and PlannedStmt.
  		 *
  		 * It's possible, and not necessarily an error, for rel->umid to be
  		 * InvalidOid even though rel->serverid is set.  That just means there
  		 * is a server with no user mapping.
  		 */
! 		if (rel->fdwroutine->NeedUserMapping())
! 		{
! 			Oid			userid  = rte->checkAsUser ? rte->checkAsUser : GetUserId();
  
! 			rel->umid = GetUserMappingId(userid, rel->serverid);
! 		}
! 		else
! 			rel->umid = InvalidOid;
  	}
  	else
  		rel->umid = InvalidOid;
***************
*** 441,450 **** build_join_rel(PlannerInfo *root,
  	 * Otherwise those fields are left invalid, so FDW API will not be called
  	 * for the join relation.
  	 *
! 	 * For FDWs like file_fdw, which ignore user mapping, the user mapping id
! 	 * associated with the joining relation may be invalid. A valid serverid
! 	 * distinguishes between a pushed down join with no user mapping and a
! 	 * join which can not be pushed down because of user mapping mismatch.
  	 */
  	if (OidIsValid(outer_rel->serverid) &&
  		inner_rel->serverid == outer_rel->serverid &&
--- 446,454 ----
  	 * Otherwise those fields are left invalid, so FDW API will not be called
  	 * for the join relation.
  	 *
! 	 * Note: if the FDW doesn't need user mappings, the user mapping OID is
! 	 * InvalidOid.  So, to give the FDW a chance to push down joins in that
! 	 * case as well, allow combining when both sides have InvalidOid.
  	 */
  	if (OidIsValid(outer_rel->serverid) &&
  		inner_rel->serverid == outer_rel->serverid &&
*** a/src/backend/utils/cache/plancache.c
--- b/src/backend/utils/cache/plancache.c
***************
*** 122,128 **** InitPlanCache(void)
  	CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
! 	/* User mapping change may invalidate plans with pushed down foreign join */
  	CacheRegisterSyscacheCallback(USERMAPPINGOID, PlanCacheUserMappingCallback, (Datum) 0);
  }
  
--- 122,128 ----
  	CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
! 	/* User mapping change may invalidate plans with user mappings */
  	CacheRegisterSyscacheCallback(USERMAPPINGOID, PlanCacheUserMappingCallback, (Datum) 0);
  }
  
***************
*** 610,624 **** 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 down, or change which user mapping is
! 	 * used.
  	 */
  	if (plansource->is_valid &&
  		plansource->gplan &&
! 		plansource->gplan->has_foreign_join &&
  		plansource->planUserId != GetUserId())
  		plansource->gplan->is_valid = false;
  
--- 610,622 ----
  		plansource->is_valid = false;
  
  	/*
! 	 * If the plan uses user mappings and the current user is different from
! 	 * the one for which the plan was created, force a replan since the user
! 	 * mapping for the new user might change.
  	 */
  	if (plansource->is_valid &&
  		plansource->gplan &&
! 		plansource->gplan->uses_user_mapping &&
  		plansource->planUserId != GetUserId())
  		plansource->gplan->is_valid = false;
  
***************
*** 1011,1027 **** BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
  	plan->is_valid = true;
  
  	/*
! 	 * Walk through the plist and set hasForeignJoin if any of the plans have
! 	 * it set.
  	 */
! 	plan->has_foreign_join = false;
  	foreach(lc, plist)
  	{
  		PlannedStmt *plan_stmt = (PlannedStmt *) lfirst(lc);
  
  		if (IsA(plan_stmt, PlannedStmt))
! 			plan->has_foreign_join =
! 				plan->has_foreign_join || plan_stmt->hasForeignJoin;
  	}
  
  	/* assign generation number to new plan */
--- 1009,1025 ----
  	plan->is_valid = true;
  
  	/*
! 	 * Walk through the plist and set uses_user_mapping if any of the plans
! 	 * have it set.
  	 */
! 	plan->uses_user_mapping = false;
  	foreach(lc, plist)
  	{
  		PlannedStmt *plan_stmt = (PlannedStmt *) lfirst(lc);
  
  		if (IsA(plan_stmt, PlannedStmt))
! 			plan->uses_user_mapping =
! 				plan->uses_user_mapping || plan_stmt->usesUserMapping;
  	}
  
  	/* assign generation number to new plan */
***************
*** 1891,1897 **** PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
   * PlanCacheUserMappingCallback
   *		Syscache inval callback function for user mapping cache invalidation.
   *
!  *	Invalidates plans which have pushed down foreign joins.
   */
  static void
  PlanCacheUserMappingCallback(Datum arg, int cacheid, uint32 hashvalue)
--- 1889,1895 ----
   * PlanCacheUserMappingCallback
   *		Syscache inval callback function for user mapping cache invalidation.
   *
!  *	Invalidates plans which use user mappings.
   */
  static void
  PlanCacheUserMappingCallback(Datum arg, int cacheid, uint32 hashvalue)
***************
*** 1911,1922 **** PlanCacheUserMappingCallback(Datum arg, int cacheid, uint32 hashvalue)
  			continue;
  
  		/*
! 		 * If the plan has pushed down foreign joins, those join may become
! 		 * unsafe to push down because of user mapping changes. Invalidate
! 		 * only the generic plan, since changes to user mapping do not
! 		 * invalidate the parse tree.
  		 */
! 		if (plansource->gplan && plansource->gplan->has_foreign_join)
  			plansource->gplan->is_valid = false;
  	}
  }
--- 1909,1918 ----
  			continue;
  
  		/*
! 		 * Invalidate only the generic plan, since changes to user mapping do
! 		 * not invalidate the parse tree.
  		 */
! 		if (plansource->gplan && plansource->gplan->uses_user_mapping)
  			plansource->gplan->is_valid = false;
  	}
  }
*** a/src/include/foreign/fdwapi.h
--- b/src/include/foreign/fdwapi.h
***************
*** 52,57 **** typedef void (*ReScanForeignScan_function) (ForeignScanState *node);
--- 52,59 ----
  
  typedef void (*EndForeignScan_function) (ForeignScanState *node);
  
+ typedef bool (*NeedUserMapping_function) (void);
+ 
  typedef void (*GetForeignJoinPaths_function) (PlannerInfo *root,
  														  RelOptInfo *joinrel,
  														RelOptInfo *outerrel,
***************
*** 177,182 **** typedef struct FdwRoutine
--- 179,185 ----
  	IterateForeignScan_function IterateForeignScan;
  	ReScanForeignScan_function ReScanForeignScan;
  	EndForeignScan_function EndForeignScan;
+ 	NeedUserMapping_function NeedUserMapping;
  
  	/*
  	 * Remaining functions are optional.  Set the pointer to NULL for any that
*** a/src/include/foreign/foreign.h
--- b/src/include/foreign/foreign.h
***************
*** 72,78 **** typedef struct ForeignTable
  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, bool missing_ok);
  extern UserMapping *GetUserMappingById(Oid umid);
  extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
  extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
--- 72,78 ----
  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,
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 73,79 **** 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 */
--- 73,80 ----
  	bool		hasRowSecurity; /* row security applied? */
  
  	bool		parallelModeNeeded;		/* parallel mode required to execute? */
! 
! 	bool		usesUserMapping;	/* does use any user mapping */
  } PlannedStmt;
  
  /* macro for fetching the Plan associated with a SubPlan node */
***************
*** 534,539 **** typedef struct ForeignScan
--- 535,541 ----
  	Scan		scan;
  	CmdType		operation;		/* SELECT/INSERT/UPDATE/DELETE */
  	Oid			fs_server;		/* OID of foreign server */
+ 	Oid			fs_usermapping;	/* OID of user mapping */
  	List	   *fdw_exprs;		/* expressions that FDW may evaluate */
  	List	   *fdw_private;	/* private data for FDW */
  	List	   *fdw_scan_tlist; /* optional tlist describing scan tuple */
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 127,133 **** typedef struct PlannerGlobal
  
  	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 */
--- 127,133 ----
  
  	bool		parallelModeNeeded;		/* parallel mode actually required? */
  
! 	bool		usesUserMapping;	/* does use any user mapping */
  } PlannerGlobal;
  
  /* macro for fetching the Plan associated with a SubPlan node */
*** a/src/include/utils/plancache.h
--- b/src/include/utils/plancache.h
***************
*** 135,141 **** typedef struct CachedPlan
  								 * changes from this value */
  	int			generation;		/* parent's generation number for this plan */
  	int			refcount;		/* count of live references to this struct */
! 	bool		has_foreign_join;		/* plan has pushed down a foreign join */
  	MemoryContext context;		/* context containing this CachedPlan */
  } CachedPlan;
  
--- 135,141 ----
  								 * changes from this value */
  	int			generation;		/* parent's generation number for this plan */
  	int			refcount;		/* count of live references to this struct */
! 	bool		uses_user_mapping;	/* does CachedPlan use any user mapping */
  	MemoryContext context;		/* context containing this CachedPlan */
  } CachedPlan;
  
