foreign_plan_cache_inval_v2.patch
binary/octet-stream
Filename: foreign_plan_cache_inval_v2.patch
Type: binary/octet-stream
Part: 0
Patch
Format: context
Series: patch v2
| File | + | − |
|---|---|---|
| contrib/postgres_fdw/expected/postgres_fdw.out | 187 | 0 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 31 | 0 |
| src/backend/optimizer/plan/setrefs.c | 58 | 0 |
| src/backend/utils/cache/plancache.c | 78 | 9 |
*** 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, that would be done in the processing of its baserels, so skip
+ * that.
+ */
+ 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 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
***************
*** 103,108 **** static bool ScanQueryWalker(Node *node, bool *acquire);
--- 104,110 ----
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 PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
***************
*** 116,121 **** InitPlanCache(void)
--- 118,126 ----
{
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);
***************
*** 1829,1834 **** PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue)
--- 1834,1903 ----
}
/*
+ * 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)
+ {
+ ListCell *lc;
+
+ 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.
+ */
+ 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 */
+ }
+ }
+ }
+ }
+
+ /*
* PlanCacheSysCallback
* Syscache inval callback function for other caches
*