*** a/contrib/postgres_fdw/expected/postgres_fdw.out
--- b/contrib/postgres_fdw/expected/postgres_fdw.out
***************
*** 2487,2498 **** EXECUTE st5('foo', 1);
--- 2487,2685 ----
    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 SELECT * FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1);
+ PREPARE st8 AS SELECT * FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) FOR UPDATE OF t1;
+ PREPARE st9  AS UPDATE ft4 SET c1 = c1;
+ PREPARE st10 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                                                              
+ -------------------------------------------------------------------------------------------------------------------------------------
+  Foreign Scan
+    Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3
+    Relations: (public.ft4 t1) LEFT JOIN (public.ft5 t2)
+    Remote SQL: SELECT r1.c1, r1.c2, r1.c3, r2.c1, r2.c2, r2.c3 FROM ("S 1"."T 3" r1 LEFT JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1))))
+ (4 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+                                                                                                                                                QUERY PLAN                                                                                                                                               
+ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  LockRows
+    Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3, t1.*, t2.*
+    ->  Foreign Scan
+          Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3, t1.*, t2.*
+          Relations: (public.ft4 t1) LEFT JOIN (public.ft5 t2)
+          Remote SQL: SELECT r1.c1, r1.c2, r1.c3, CASE WHEN (r1.*)::text IS NOT NULL THEN ROW(r1.c1, r1.c2, r1.c3) END, r2.c1, r2.c2, r2.c3, CASE WHEN (r2.*)::text IS NOT NULL THEN ROW(r2.c1, r2.c2, r2.c3) END FROM ("S 1"."T 3" r1 LEFT JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1)))) FOR UPDATE OF r1
+          ->  Hash Left Join
+                Output: t1.c1, t1.c2, t1.c3, t1.*, t2.c1, t2.c2, t2.c3, t2.*
+                Hash Cond: (t1.c1 = t2.c1)
+                ->  Foreign Scan on public.ft4 t1
+                      Output: t1.c1, t1.c2, t1.c3, t1.*
+                      Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3" FOR UPDATE
+                ->  Hash
+                      Output: t2.c1, t2.c2, t2.c3, t2.*
+                      ->  Foreign Scan on public.ft5 t2
+                            Output: t2.c1, t2.c2, t2.c3, t2.*
+                            Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 4"
+ (17 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
+                      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 st10;
+                              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');
+ ALTER FOREIGN TABLE ft5 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 4"
+ (3 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
+                                                              QUERY PLAN                                                              
+ -------------------------------------------------------------------------------------------------------------------------------------
+  Foreign Scan
+    Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3
+    Relations: (public.ft4 t1) LEFT JOIN (public.ft5 t2)
+    Remote SQL: SELECT r1.c1, r1.c2, r1.c3, r2.c1, r2.c2, r2.c3 FROM ("S 1"."T 4" r1 LEFT JOIN "S 1"."T 3" r2 ON (((r1.c1 = r2.c1))))
+ (4 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+                                                                                                                                                QUERY PLAN                                                                                                                                               
+ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  LockRows
+    Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3, t1.*, t2.*
+    ->  Foreign Scan
+          Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3, t1.*, t2.*
+          Relations: (public.ft4 t1) LEFT JOIN (public.ft5 t2)
+          Remote SQL: SELECT r1.c1, r1.c2, r1.c3, CASE WHEN (r1.*)::text IS NOT NULL THEN ROW(r1.c1, r1.c2, r1.c3) END, r2.c1, r2.c2, r2.c3, CASE WHEN (r2.*)::text IS NOT NULL THEN ROW(r2.c1, r2.c2, r2.c3) END FROM ("S 1"."T 4" r1 LEFT JOIN "S 1"."T 3" r2 ON (((r1.c1 = r2.c1)))) FOR UPDATE OF r1
+          ->  Hash Left Join
+                Output: t1.c1, t1.c2, t1.c3, t1.*, t2.c1, t2.c2, t2.c3, t2.*
+                Hash Cond: (t1.c1 = t2.c1)
+                ->  Foreign Scan on public.ft4 t1
+                      Output: t1.c1, t1.c2, t1.c3, t1.*
+                      Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 4" FOR UPDATE
+                ->  Hash
+                      Output: t2.c1, t2.c2, t2.c3, t2.*
+                      ->  Foreign Scan on public.ft5 t2
+                            Output: t2.c1, t2.c2, t2.c3, t2.*
+                            Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3"
+ (17 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
+                      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 st10;
+                              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');
+ ALTER FOREIGN TABLE ft5 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 3"
+ (3 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7;
+                                                              QUERY PLAN                                                              
+ -------------------------------------------------------------------------------------------------------------------------------------
+  Foreign Scan
+    Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3
+    Relations: (public.ft4 t1) LEFT JOIN (public.ft5 t2)
+    Remote SQL: SELECT r1.c1, r1.c2, r1.c3, r2.c1, r2.c2, r2.c3 FROM ("S 1"."T 3" r1 LEFT JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1))))
+ (4 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8;
+                                                                                                                                                QUERY PLAN                                                                                                                                               
+ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  LockRows
+    Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3, t1.*, t2.*
+    ->  Foreign Scan
+          Output: t1.c1, t1.c2, t1.c3, t2.c1, t2.c2, t2.c3, t1.*, t2.*
+          Relations: (public.ft4 t1) LEFT JOIN (public.ft5 t2)
+          Remote SQL: SELECT r1.c1, r1.c2, r1.c3, CASE WHEN (r1.*)::text IS NOT NULL THEN ROW(r1.c1, r1.c2, r1.c3) END, r2.c1, r2.c2, r2.c3, CASE WHEN (r2.*)::text IS NOT NULL THEN ROW(r2.c1, r2.c2, r2.c3) END FROM ("S 1"."T 3" r1 LEFT JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1)))) FOR UPDATE OF r1
+          ->  Hash Left Join
+                Output: t1.c1, t1.c2, t1.c3, t1.*, t2.c1, t2.c2, t2.c3, t2.*
+                Hash Cond: (t1.c1 = t2.c1)
+                ->  Foreign Scan on public.ft4 t1
+                      Output: t1.c1, t1.c2, t1.c3, t1.*
+                      Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3" FOR UPDATE
+                ->  Hash
+                      Output: t2.c1, t2.c2, t2.c3, t2.*
+                      ->  Foreign Scan on public.ft5 t2
+                            Output: t2.c1, t2.c2, t2.c3, t2.*
+                            Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 4"
+ (17 rows)
+ 
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st9;
+                      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 st10;
+                              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;
+ DEALLOCATE st10;
  -- 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,608 ----
  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 SELECT * FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1);
+ PREPARE st8 AS SELECT * FROM ft4 t1 LEFT JOIN ft5 t2 ON (t1.c1 = t2.c1) FOR UPDATE OF t1;
+ PREPARE st9  AS UPDATE ft4 SET c1 = c1;
+ PREPARE st10 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;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st10;
+ ALTER FOREIGN TABLE ft4 OPTIONS (SET table_name 'T 4');
+ ALTER FOREIGN TABLE ft5 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;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st10;
+ -- restore the original options and check again
+ ALTER FOREIGN TABLE ft4 OPTIONS (SET table_name 'T 3');
+ ALTER FOREIGN TABLE ft5 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;
+ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st10;
  
  -- cleanup
  DEALLOCATE st1;
***************
*** 584,589 **** DEALLOCATE st2;
--- 610,620 ----
  DEALLOCATE st3;
  DEALLOCATE st4;
  DEALLOCATE st5;
+ DEALLOCATE st6;
+ DEALLOCATE st7;
+ DEALLOCATE st8;
+ DEALLOCATE st9;
+ DEALLOCATE st10;
  
  -- 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
***************
*** 17,28 ****
--- 17,30 ----
  
  #include "access/transam.h"
  #include "catalog/pg_type.h"
+ #include "foreign/foreign.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
  #include "optimizer/pathnode.h"
  #include "optimizer/planmain.h"
  #include "optimizer/planner.h"
  #include "optimizer/tlist.h"
+ #include "parser/parsetree.h"
  #include "tcop/utility.h"
  #include "utils/lsyscache.h"
  #include "utils/syscache.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(PlannerInfo *root, ForeignScan *scan);
+ static void add_inval_item(PlannerInfo *root, 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.
   *
***************
*** 1123,1128 **** set_foreignscan_references(PlannerInfo *root,
--- 1127,1140 ----
  						   ForeignScan *fscan,
  						   int rtoffset)
  {
+ 	/*
+ 	 * Record dependencies on FDW-related objects.  If an outer subplan
+ 	 * exists, the dependencies would be recorded while processing the foreign
+ 	 * scans for the base relations in the subplan.  Hence skip that here.
+ 	 */
+ 	if (fscan->scan.plan.lefttree == NULL)
+ 		record_plan_foreign_dependencies(root, fscan);
+ 
  	/* Adjust scanrelid if it's valid */
  	if (fscan->scan.scanrelid > 0)
  		fscan->scan.scanrelid += rtoffset;
***************
*** 2423,2441 **** 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);
  	}
  }
  
  /*
--- 2435,2491 ----
  	 */
  	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, 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(PlannerInfo *root, ForeignScan *scan)
! {
! 	int			relid = -1;
! 	ForeignServer *server = GetForeignServer(scan->fs_server);
  
! 	/* Record dependencies on the foreign tables */
! 	while ((relid = bms_next_member(scan->fs_relids, relid)) >= 0)
! 	{
! 		RangeTblEntry	*rte = planner_rt_fetch(relid, root);
! 
! 		add_inval_item(root, FOREIGNTABLEREL, rte->relid);
  	}
+ 
+ 	/* Likewise for the foreign server */
+ 	add_inval_item(root, FOREIGNSERVEROID, scan->fs_server);
+ 
+ 	/* Likewise for the foreign data wrapper */
+ 	add_inval_item(root, FOREIGNDATAWRAPPEROID, server->fdwid);
+ }
+ 
+ /*
+  * add_inval_item
+  *		Add given dependency to root->glob->invalItems.
+  */
+ static void
+ add_inval_item(PlannerInfo *root, int cacheid, Oid oid)
+ {
+ 	PlanInvalItem *inval_item = makeNode(PlanInvalItem);
+ 
+ 	inval_item->cacheId = cacheid;
+ 	inval_item->hashValue = GetSysCacheHashValue1(cacheid,
+ 												  ObjectIdGetDatum(oid));
+ 
+ 	root->glob->invalItems = lappend(root->glob->invalItems, inval_item);
  }
  
  /*
*** 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
***************
*** 103,108 **** static bool ScanQueryWalker(Node *node, bool *acquire);
--- 104,112 ----
  static TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
  static void PlanCacheRelCallback(Datum arg, Oid relid);
  static void PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue);
+ static void PlanCacheForeignCallback(Datum arg, int cacheid, uint32 hashvalue);
+ static void CheckGenericPlanDependencies(CachedPlanSource *plansource,
+ 							 int cacheid, uint32 hashvalue);
  static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
  
  
***************
*** 116,121 **** InitPlanCache(void)
--- 120,128 ----
  {
  	CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(PROCOID, PlanCacheFuncCallback, (Datum) 0);
+ 	CacheRegisterSyscacheCallback(FOREIGNTABLEREL, PlanCacheForeignCallback, (Datum) 0);
+ 	CacheRegisterSyscacheCallback(FOREIGNSERVEROID, PlanCacheForeignCallback, (Datum) 0);
+ 	CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, PlanCacheForeignCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
  	CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
***************
*** 1797,1828 **** PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue)
  		 * The generic plan, if any, could have more dependencies than the
  		 * querytree does, so we have to check it too.
  		 */
! 		if (plansource->gplan && plansource->gplan->is_valid)
  		{
! 			foreach(lc, plansource->gplan->stmt_list)
  			{
! 				PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
! 				ListCell   *lc3;
  
! 				Assert(!IsA(plannedstmt, Query));
! 				if (!IsA(plannedstmt, PlannedStmt))
! 					continue;	/* Ignore utility statements */
! 				foreach(lc3, plannedstmt->invalItems)
  				{
! 					PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3);
! 
! 					if (item->cacheId != cacheid)
! 						continue;
! 					if (hashvalue == 0 ||
! 						item->hashValue == hashvalue)
! 					{
! 						/* Invalidate the generic plan only */
! 						plansource->gplan->is_valid = false;
! 						break;	/* out of invalItems scan */
! 					}
  				}
- 				if (!plansource->gplan->is_valid)
- 					break;		/* out of stmt_list scan */
  			}
  		}
  	}
--- 1804,1882 ----
  		 * The generic plan, if any, could have more dependencies than the
  		 * querytree does, so we have to check it too.
  		 */
! 		CheckGenericPlanDependencies(plansource, cacheid, hashvalue);
! 	}
! }
! 
! /*
!  * PlanCacheForeignCallback
!  *		Syscache inval callback function for FOREIGNTABLEREL, FOREIGNSERVEROID
!  *		and FOREIGNDATAWRAPPEROID caches
!  *
!  * Invalidate all plans mentioning the object with the specified hash value,
!  * or all plans mentioning any member of the given cache if hashvalue == 0.
!  *
!  * Note: any updates on those catalogs don't affect the rewritten querytree,
!  * but might require replanning, so we invalidate the generic plan only.
!  */
! static void
! PlanCacheForeignCallback(Datum arg, int cacheid, uint32 hashvalue)
! {
! 	CachedPlanSource *plansource;
! 
! 	for (plansource = first_saved_plan; plansource; plansource = plansource->next_saved)
! 	{
! 		Assert(plansource->magic == CACHEDPLANSOURCE_MAGIC);
! 
! 		/* No work if it's already invalidated */
! 		if (!plansource->is_valid)
! 			continue;
! 
! 		/* Never invalidate transaction control commands */
! 		if (IsTransactionStmtPlan(plansource))
! 			continue;
! 
! 		/*
! 		 * Check the dependency list for the generic plan.
! 		 */
! 		CheckGenericPlanDependencies(plansource, cacheid, hashvalue);
! 	}
! }
! 
! /*
!  * CheckGenericPlanDependencies: invalidate the generic plan, if it meets the
!  * condition described in the comments for PlanCacheFuncCallback and
!  * PlanCacheForeignCallback.
!  */
! static void
! CheckGenericPlanDependencies(CachedPlanSource *plansource,
! 							 int cacheid, uint32 hashvalue)
! {
! 	if (plansource->gplan && plansource->gplan->is_valid)
! 	{
! 		ListCell   *lc;
! 
! 		foreach(lc, plansource->gplan->stmt_list)
  		{
! 			PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc);
! 			ListCell   *lc3;
! 
! 			Assert(!IsA(plannedstmt, Query));
! 			if (!IsA(plannedstmt, PlannedStmt))
! 				continue;	/* Ignore utility statements */
! 			foreach(lc3, plannedstmt->invalItems)
  			{
! 				PlanInvalItem *item = (PlanInvalItem *) lfirst(lc3);
  
! 				if (item->cacheId != cacheid)
! 					continue;
! 				if (hashvalue == 0 ||
! 					item->hashValue == hashvalue)
  				{
! 					/* Invalidate the generic plan only */
! 					plansource->gplan->is_valid = false;
! 					return;
  				}
  			}
  		}
  	}
