*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 2487,2498 **** EXECUTE st5('foo', 1);
--- 2487,2615 ----
    1 |  1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1  | 1          | foo
  (1 row)
  
+ -- changing foreign table options should change the plans
+ PREPARE st6 AS SELECT * FROM ft4;
+ PREPARE st7 AS INSERT INTO ft4 VALUES (101, 102, 'foo');
+ PREPARE st8 AS UPDATE ft4 SET c1 = c1;
+ PREPARE st9 AS UPDATE ft4 SET c1 = random();
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6;
+                     QUERY PLAN                    
+ --------------------------------------------------
+  Foreign Scan on public.ft4
+    Output: c1, c2, c3
+    Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3"
+ (3 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
+                               QUERY PLAN                               
+ -----------------------------------------------------------------------
+  Insert on public.ft4
+    Remote SQL: INSERT INTO "S 1"."T 3"(c1, c2, c3) VALUES ($1, $2, $3)
+    ->  Result
+          Output: 101, 102, 'foo'::text
+ (4 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+                      QUERY PLAN                     
+ ----------------------------------------------------
+  Update on public.ft4
+    ->  Foreign Update on public.ft4
+          Remote SQL: UPDATE "S 1"."T 3" SET c1 = c1
+ (3 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
+                              QUERY PLAN                              
+ ---------------------------------------------------------------------
+  Update on public.ft4
+    Remote SQL: UPDATE "S 1"."T 3" SET c1 = $2 WHERE ctid = $1
+    ->  Foreign Scan on public.ft4
+          Output: random(), c2, c3, ctid
+          Remote SQL: SELECT c2, c3, ctid FROM "S 1"."T 3" FOR UPDATE
+ (5 rows)
+ 
+ ALTER FOREIGN TABLE ft4 OPTIONS (SET table_name 'T 4');
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6;
+                     QUERY PLAN                    
+ --------------------------------------------------
+  Foreign Scan on public.ft4
+    Output: c1, c2, c3
+    Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 4"
+ (3 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
+                               QUERY PLAN                               
+ -----------------------------------------------------------------------
+  Insert on public.ft4
+    Remote SQL: INSERT INTO "S 1"."T 4"(c1, c2, c3) VALUES ($1, $2, $3)
+    ->  Result
+          Output: 101, 102, 'foo'::text
+ (4 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+                      QUERY PLAN                     
+ ----------------------------------------------------
+  Update on public.ft4
+    ->  Foreign Update on public.ft4
+          Remote SQL: UPDATE "S 1"."T 4" SET c1 = c1
+ (3 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
+                              QUERY PLAN                              
+ ---------------------------------------------------------------------
+  Update on public.ft4
+    Remote SQL: UPDATE "S 1"."T 4" SET c1 = $2 WHERE ctid = $1
+    ->  Foreign Scan on public.ft4
+          Output: random(), c2, c3, ctid
+          Remote SQL: SELECT c2, c3, ctid FROM "S 1"."T 4" FOR UPDATE
+ (5 rows)
+ 
+ -- restore the original options and check again
+ ALTER FOREIGN TABLE ft4 OPTIONS (SET table_name 'T 3');
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6;
+                     QUERY PLAN                    
+ --------------------------------------------------
+  Foreign Scan on public.ft4
+    Output: c1, c2, c3
+    Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3"
+ (3 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
+                               QUERY PLAN                               
+ -----------------------------------------------------------------------
+  Insert on public.ft4
+    Remote SQL: INSERT INTO "S 1"."T 3"(c1, c2, c3) VALUES ($1, $2, $3)
+    ->  Result
+          Output: 101, 102, 'foo'::text
+ (4 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+                      QUERY PLAN                     
+ ----------------------------------------------------
+  Update on public.ft4
+    ->  Foreign Update on public.ft4
+          Remote SQL: UPDATE "S 1"."T 3" SET c1 = c1
+ (3 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
+                              QUERY PLAN                              
+ ---------------------------------------------------------------------
+  Update on public.ft4
+    Remote SQL: UPDATE "S 1"."T 3" SET c1 = $2 WHERE ctid = $1
+    ->  Foreign Scan on public.ft4
+          Output: random(), c2, c3, ctid
+          Remote SQL: SELECT c2, c3, ctid FROM "S 1"."T 3" FOR UPDATE
+ (5 rows)
+ 
  -- cleanup
  DEALLOCATE st1;
  DEALLOCATE st2;
  DEALLOCATE st3;
  DEALLOCATE st4;
  DEALLOCATE st5;
+ DEALLOCATE st6;
+ DEALLOCATE st7;
+ DEALLOCATE st8;
+ DEALLOCATE st9;
  -- System columns, except ctid and oid, should not be sent to remote
  EXPLAIN (VERBOSE, COSTS OFF)
  SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1;
*** a/contrib/postgres_fdw/sql/postgres_fdw.sql
--- b/contrib/postgres_fdw/sql/postgres_fdw.sql
***************
*** 577,582 **** EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
--- 577,602 ----
  EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
  EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1);
  EXECUTE st5('foo', 1);
+ -- changing foreign table options should change the plans
+ PREPARE st6 AS SELECT * FROM ft4;
+ PREPARE st7 AS INSERT INTO ft4 VALUES (101, 102, 'foo');
+ PREPARE st8 AS UPDATE ft4 SET c1 = c1;
+ PREPARE st9 AS UPDATE ft4 SET c1 = random();
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
+ ALTER FOREIGN TABLE ft4 OPTIONS (SET table_name 'T 4');
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
+ -- restore the original options and check again
+ ALTER FOREIGN TABLE ft4 OPTIONS (SET table_name 'T 3');
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
  
  -- cleanup
  DEALLOCATE st1;
***************
*** 584,589 **** DEALLOCATE st2;
--- 604,613 ----
  DEALLOCATE st3;
  DEALLOCATE st4;
  DEALLOCATE st5;
+ DEALLOCATE st6;
+ DEALLOCATE st7;
+ DEALLOCATE st8;
+ DEALLOCATE st9;
  
  -- System columns, except ctid and oid, should not be sent to remote
  EXPLAIN (VERBOSE, COSTS OFF)
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
***************
*** 16,22 ****
--- 16,24 ----
  #include "postgres.h"
  
  #include "access/transam.h"
+ #include "catalog/pg_class.h"
  #include "catalog/pg_type.h"
+ #include "foreign/foreign.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
  #include "optimizer/pathnode.h"
***************
*** 137,142 **** static List *set_returning_clause_references(PlannerInfo *root,
--- 139,146 ----
  								Plan *topplan,
  								Index resultRelation,
  								int rtoffset);
+ static void record_plan_foreign_dependencies(PlannerGlobal *glob, Oid relid);
+ static void add_inval_item(PlannerGlobal *glob, int cacheid, Oid oid);
  static bool extract_query_dependencies_walker(Node *node,
  								  PlannerInfo *context);
  
***************
*** 174,181 **** static bool extract_query_dependencies_walker(Node *node,
   * This will be used by plancache.c to drive invalidation of cached plans.
   * Relation dependencies are represented by OIDs, and everything else by
   * PlanInvalItems (this distinction is motivated by the shared-inval APIs).
!  * Currently, relations and user-defined functions are the only types of
!  * objects that are explicitly tracked this way.
   *
   * 8. We assign every plan node in the tree a unique ID.
   *
--- 178,185 ----
   * This will be used by plancache.c to drive invalidation of cached plans.
   * Relation dependencies are represented by OIDs, and everything else by
   * PlanInvalItems (this distinction is motivated by the shared-inval APIs).
!  * Currently, relations, user-defined functions and FDW-related objects are
!  * the only types of objects that are explicitly tracked this way.
   *
   * 8. We assign every plan node in the tree a unique ID.
   *
***************
*** 426,432 **** add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
--- 430,442 ----
  	 * but it would probably cost more cycles than it would save.
  	 */
  	if (newrte->rtekind == RTE_RELATION)
+ 	{
  		glob->relationOids = lappend_oid(glob->relationOids, newrte->relid);
+ 
+ 		/* Collect dependencies on FDW-related objects too */
+ 		if (newrte->relkind == RELKIND_FOREIGN_TABLE)
+ 			record_plan_foreign_dependencies(glob, newrte->relid);
+ 	}
  }
  
  /*
***************
*** 2423,2444 **** record_plan_function_dependency(PlannerInfo *root, Oid funcid)
  	 */
  	if (funcid >= (Oid) FirstBootstrapObjectId)
  	{
- 		PlanInvalItem *inval_item = makeNode(PlanInvalItem);
- 
  		/*
  		 * It would work to use any syscache on pg_proc, but the easiest is
  		 * PROCOID since we already have the function's OID at hand.  Note
  		 * that plancache.c knows we use PROCOID.
  		 */
! 		inval_item->cacheId = PROCOID;
! 		inval_item->hashValue = GetSysCacheHashValue1(PROCOID,
! 												   ObjectIdGetDatum(funcid));
! 
! 		root->glob->invalItems = lappend(root->glob->invalItems, inval_item);
  	}
  }
  
  /*
   * extract_query_dependencies
   *		Given a rewritten, but not yet planned, query or queries
   *		(i.e. a Query node or list of Query nodes), extract dependencies
--- 2433,2487 ----
  	 */
  	if (funcid >= (Oid) FirstBootstrapObjectId)
  	{
  		/*
  		 * It would work to use any syscache on pg_proc, but the easiest is
  		 * PROCOID since we already have the function's OID at hand.  Note
  		 * that plancache.c knows we use PROCOID.
  		 */
! 		add_inval_item(root->glob, PROCOID, funcid);
  	}
  }
  
  /*
+  * record_plan_foreign_dependencies
+  *		Mark the current plan as depending on FDW-related objects.
+  *
+  * This is required since modifications to attributes of FDW-related objects,
+  * such as FDW options, might require replanning.
+  */
+ static void
+ record_plan_foreign_dependencies(PlannerGlobal *glob, Oid relid)
+ {
+ 	ForeignTable *table = GetForeignTable(relid);
+ 	ForeignServer *server = GetForeignServer(table->serverid);
+ 
+ 	/* Record the dependency on the foreign table */
+ 	add_inval_item(glob, FOREIGNTABLEREL, relid);
+ 
+ 	/* Likewise for the foreign server */
+ 	add_inval_item(glob, FOREIGNSERVEROID, table->serverid);
+ 
+ 	/* Likewise for the foreign data wrapper */
+ 	add_inval_item(glob, FOREIGNDATAWRAPPEROID, server->fdwid);
+ }
+ 
+ /*
+  * add_inval_item
+  *		Add given dependency to glob->invalItems.
+  */
+ static void
+ add_inval_item(PlannerGlobal *glob, int cacheid, Oid oid)
+ {
+ 	PlanInvalItem *inval_item = makeNode(PlanInvalItem);
+ 
+ 	inval_item->cacheId = cacheid;
+ 	inval_item->hashValue = GetSysCacheHashValue1(cacheid,
+ 												  ObjectIdGetDatum(oid));
+ 
+ 	glob->invalItems = lappend(glob->invalItems, inval_item);
+ }
+ 
+ /*
   * extract_query_dependencies
   *		Given a rewritten, but not yet planned, query or queries
   *		(i.e. a Query node or list of Query nodes), extract dependencies
***************
*** 2510,2517 **** extract_query_dependencies_walker(Node *node, PlannerInfo *context)
--- 2553,2566 ----
  			RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
  
  			if (rte->rtekind == RTE_RELATION)
+ 			{
  				context->glob->relationOids =
  					lappend_oid(context->glob->relationOids, rte->relid);
+ 
+ 				/* Collect dependencies on FDW-related objects too */
+ 				if (rte->relkind == RELKIND_FOREIGN_TABLE)
+ 					record_plan_foreign_dependencies(context->glob, rte->relid);
+ 			}
  		}
  
  		/* And recurse into the query's subexpressions */
*** a/src/backend/utils/cache/plancache.c
--- b/src/backend/utils/cache/plancache.c
***************
*** 27,41 ****
   * query to change output tupdesc on replan --- if so, it's up to the
   * caller to notice changes and cope with them.
   *
!  * Currently, we track exactly the dependencies of plans on relations and
!  * user-defined functions.  On relcache invalidation events or pg_proc
!  * syscache invalidation events, we invalidate just those plans that depend
!  * on the particular object being modified.  (Note: this scheme assumes
!  * that any table modification that requires replanning will generate a
!  * relcache inval event.)  We also watch for inval events on certain other
!  * system catalogs, such as pg_namespace; but for them, our response is
!  * just to invalidate all plans.  We expect updates on those catalogs to
!  * be infrequent enough that more-detailed tracking is not worth the effort.
   *
   *
   * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
--- 27,42 ----
   * query to change output tupdesc on replan --- if so, it's up to the
   * caller to notice changes and cope with them.
   *
!  * Currently, we track exactly the dependencies of plans on relations,
!  * user-defined functions and FDW-related objects.  On relcache invalidation
!  * events or the relevant syscache invalidation events, we invalidate just
!  * those plans that depend on the particular object being modified.  (Note:
!  * this scheme assumes that any table modification that requires replanning
!  * will generate a relcache inval event.)  We also watch for inval events on
!  * certain other system catalogs, such as pg_namespace; but for them, our
!  * response is just to invalidate all plans.  We expect updates on those
!  * catalogs to be infrequent enough that more-detailed tracking is not worth
!  * the effort.
   *
   *
   * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
***************
*** 116,121 **** InitPlanCache(void)
--- 117,125 ----
  {
  	CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(PROCOID, PlanCacheFuncCallback, (Datum) 0);
+ 	CacheRegisterSyscacheCallback(FOREIGNTABLEREL, PlanCacheFuncCallback, (Datum) 0);
+ 	CacheRegisterSyscacheCallback(FOREIGNSERVEROID, PlanCacheFuncCallback, (Datum) 0);
+ 	CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, PlanCacheFuncCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
