@@ -104,6 +104,8 @@ static TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
104104static void PlanCacheRelCallback (Datum arg , Oid relid );
105105static void PlanCacheFuncCallback (Datum arg , int cacheid , uint32 hashvalue );
106106static void PlanCacheSysCallback (Datum arg , int cacheid , uint32 hashvalue );
107+ static void PlanCacheUserMappingCallback (Datum arg , int cacheid ,
108+ uint32 hashvalue );
107109
108110
109111/*
@@ -119,6 +121,8 @@ InitPlanCache(void)
119121 CacheRegisterSyscacheCallback (NAMESPACEOID , PlanCacheSysCallback , (Datum ) 0 );
120122 CacheRegisterSyscacheCallback (OPEROID , PlanCacheSysCallback , (Datum ) 0 );
121123 CacheRegisterSyscacheCallback (AMOPOPID , PlanCacheSysCallback , (Datum ) 0 );
124+ /* User mapping change may invalidate plans with pushed down foreign join */
125+ CacheRegisterSyscacheCallback (USERMAPPINGOID , PlanCacheUserMappingCallback , (Datum ) 0 );
122126}
123127
124128/*
@@ -574,7 +578,8 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
574578 /*
575579 * If this is a new cached plan, then set the user id it was planned by
576580 * and under what row security settings; these are needed to determine
577- * plan invalidation when RLS is involved.
581+ * plan invalidation when RLS is involved or foreign joins are pushed
582+ * down.
578583 */
579584 if (!OidIsValid (plansource -> planUserId ))
580585 {
@@ -609,6 +614,18 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
609614 || plansource -> row_security_env != row_security ))
610615 plansource -> is_valid = false;
611616
617+ /*
618+ * If we have a join pushed down to the foreign server and the current user
619+ * is different from the one for which the plan was created, invalidate the
620+ * generic plan since user mapping for the new user might make the join
621+ * unsafe to push down, or change which user mapping is used.
622+ */
623+ if (plansource -> is_valid &&
624+ plansource -> gplan &&
625+ plansource -> gplan -> has_foreign_join &&
626+ plansource -> planUserId != GetUserId ())
627+ plansource -> gplan -> is_valid = false;
628+
612629 /*
613630 * If the query is currently valid, acquire locks on the referenced
614631 * objects; then check again. We need to do it this way to cover the race
@@ -881,6 +898,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
881898 bool spi_pushed ;
882899 MemoryContext plan_context ;
883900 MemoryContext oldcxt = CurrentMemoryContext ;
901+ ListCell * lc ;
884902
885903 /*
886904 * Normally the querytree should be valid already, but if it's not,
@@ -988,6 +1006,20 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
9881006 plan -> is_saved = false;
9891007 plan -> is_valid = true;
9901008
1009+ /*
1010+ * Walk through the plist and set hasForeignJoin if any of the plans have
1011+ * it set.
1012+ */
1013+ plan -> has_foreign_join = false;
1014+ foreach (lc , plist )
1015+ {
1016+ PlannedStmt * plan_stmt = (PlannedStmt * ) lfirst (lc );
1017+
1018+ if (IsA (plan_stmt , PlannedStmt ))
1019+ plan -> has_foreign_join =
1020+ plan -> has_foreign_join || plan_stmt -> hasForeignJoin ;
1021+ }
1022+
9911023 /* assign generation number to new plan */
9921024 plan -> generation = ++ (plansource -> generation );
9931025
@@ -1843,6 +1875,40 @@ PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
18431875 ResetPlanCache ();
18441876}
18451877
1878+ /*
1879+ * PlanCacheUserMappingCallback
1880+ * Syscache inval callback function for user mapping cache invalidation.
1881+ *
1882+ * Invalidates plans which have pushed down foreign joins.
1883+ */
1884+ static void
1885+ PlanCacheUserMappingCallback (Datum arg , int cacheid , uint32 hashvalue )
1886+ {
1887+ CachedPlanSource * plansource ;
1888+
1889+ for (plansource = first_saved_plan ; plansource ; plansource = plansource -> next_saved )
1890+ {
1891+ Assert (plansource -> magic == CACHEDPLANSOURCE_MAGIC );
1892+
1893+ /* No work if it's already invalidated */
1894+ if (!plansource -> is_valid )
1895+ continue ;
1896+
1897+ /* Never invalidate transaction control commands */
1898+ if (IsTransactionStmtPlan (plansource ))
1899+ continue ;
1900+
1901+ /*
1902+ * If the plan has pushed down foreign joins, those join may become
1903+ * unsafe to push down because of user mapping changes. Invalidate only
1904+ * the generic plan, since changes to user mapping do not invalidate the
1905+ * parse tree.
1906+ */
1907+ if (plansource -> gplan && plansource -> gplan -> has_foreign_join )
1908+ plansource -> gplan -> is_valid = false;
1909+ }
1910+ }
1911+
18461912/*
18471913 * ResetPlanCache: invalidate all cached plans.
18481914 */
0 commit comments