summaryrefslogtreecommitdiff
path: root/src/backend/utils/cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r--src/backend/utils/cache/plancache.c68
1 files changed, 67 insertions, 1 deletions
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 539f4b9240c..a93825d0087 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -104,6 +104,8 @@ static TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
static void PlanCacheRelCallback(Datum arg, Oid relid);
static void PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue);
static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
+static void PlanCacheUserMappingCallback(Datum arg, int cacheid,
+ uint32 hashvalue);
/*
@@ -119,6 +121,8 @@ 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);
}
/*
@@ -574,7 +578,8 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
/*
* If this is a new cached plan, then set the user id it was planned by
* and under what row security settings; these are needed to determine
- * plan invalidation when RLS is involved.
+ * plan invalidation when RLS is involved or foreign joins are pushed
+ * down.
*/
if (!OidIsValid(plansource->planUserId))
{
@@ -610,6 +615,18 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
plansource->is_valid = false;
/*
+ * If we have a join pushed down to the foreign server and the current user
+ * is different from the one for which the plan was created, invalidate the
+ * generic plan since user mapping for the new user might make the join
+ * unsafe to push 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;
+
+ /*
* If the query is currently valid, acquire locks on the referenced
* objects; then check again. We need to do it this way to cover the race
* condition that an invalidation message arrives before we get the locks.
@@ -881,6 +898,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
bool spi_pushed;
MemoryContext plan_context;
MemoryContext oldcxt = CurrentMemoryContext;
+ ListCell *lc;
/*
* Normally the querytree should be valid already, but if it's not,
@@ -988,6 +1006,20 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
plan->is_saved = false;
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 */
plan->generation = ++(plansource->generation);
@@ -1844,6 +1876,40 @@ 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)
+{
+ 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;
+
+ /*
+ * 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;
+ }
+}
+
+/*
* ResetPlanCache: invalidate all cached plans.
*/
void