@@ -104,6 +104,8 @@ static TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
104
104
static void PlanCacheRelCallback (Datum arg , Oid relid );
105
105
static void PlanCacheFuncCallback (Datum arg , int cacheid , uint32 hashvalue );
106
106
static void PlanCacheSysCallback (Datum arg , int cacheid , uint32 hashvalue );
107
+ static void PlanCacheUserMappingCallback (Datum arg , int cacheid ,
108
+ uint32 hashvalue );
107
109
108
110
109
111
/*
@@ -119,6 +121,8 @@ InitPlanCache(void)
119
121
CacheRegisterSyscacheCallback (NAMESPACEOID , PlanCacheSysCallback , (Datum ) 0 );
120
122
CacheRegisterSyscacheCallback (OPEROID , PlanCacheSysCallback , (Datum ) 0 );
121
123
CacheRegisterSyscacheCallback (AMOPOPID , PlanCacheSysCallback , (Datum ) 0 );
124
+ /* User mapping change may invalidate plans with pushed down foreign join */
125
+ CacheRegisterSyscacheCallback (USERMAPPINGOID , PlanCacheUserMappingCallback , (Datum ) 0 );
122
126
}
123
127
124
128
/*
@@ -574,7 +578,8 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
574
578
/*
575
579
* If this is a new cached plan, then set the user id it was planned by
576
580
* 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.
578
583
*/
579
584
if (!OidIsValid (plansource -> planUserId ))
580
585
{
@@ -609,6 +614,18 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
609
614
|| plansource -> row_security_env != row_security ))
610
615
plansource -> is_valid = false;
611
616
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
+
612
629
/*
613
630
* If the query is currently valid, acquire locks on the referenced
614
631
* objects; then check again. We need to do it this way to cover the race
@@ -881,6 +898,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
881
898
bool spi_pushed ;
882
899
MemoryContext plan_context ;
883
900
MemoryContext oldcxt = CurrentMemoryContext ;
901
+ ListCell * lc ;
884
902
885
903
/*
886
904
* Normally the querytree should be valid already, but if it's not,
@@ -988,6 +1006,20 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
988
1006
plan -> is_saved = false;
989
1007
plan -> is_valid = true;
990
1008
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
+
991
1023
/* assign generation number to new plan */
992
1024
plan -> generation = ++ (plansource -> generation );
993
1025
@@ -1843,6 +1875,40 @@ PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
1843
1875
ResetPlanCache ();
1844
1876
}
1845
1877
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
+
1846
1912
/*
1847
1913
* ResetPlanCache: invalidate all cached plans.
1848
1914
*/
0 commit comments