diff options
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r-- | src/backend/utils/cache/plancache.c | 178 |
1 files changed, 55 insertions, 123 deletions
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 005e4b7f1c3..f42a62d5000 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -100,13 +100,10 @@ static void AcquireExecutorLocks(List *stmt_list, bool acquire); static void AcquirePlannerLocks(List *stmt_list, bool acquire); static void ScanQueryForLocks(Query *parsetree, bool acquire); static bool ScanQueryWalker(Node *node, bool *acquire); -static bool plan_list_is_transient(List *stmt_list); 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); /* @@ -122,8 +119,6 @@ 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); } /* @@ -198,6 +193,9 @@ CreateCachedPlan(Node *raw_parse_tree, plansource->invalItems = NIL; plansource->search_path = NULL; plansource->query_context = NULL; + plansource->rewriteRoleId = InvalidOid; + plansource->rewriteRowSecurity = false; + plansource->dependsOnRLS = false; plansource->gplan = NULL; plansource->is_oneshot = false; plansource->is_complete = false; @@ -208,9 +206,6 @@ CreateCachedPlan(Node *raw_parse_tree, plansource->generic_cost = -1; plansource->total_custom_cost = 0; plansource->num_custom_plans = 0; - plansource->hasRowSecurity = false; - plansource->planUserId = InvalidOid; - plansource->row_security_env = false; MemoryContextSwitchTo(oldcxt); @@ -266,6 +261,9 @@ CreateOneShotCachedPlan(Node *raw_parse_tree, plansource->invalItems = NIL; plansource->search_path = NULL; plansource->query_context = NULL; + plansource->rewriteRoleId = InvalidOid; + plansource->rewriteRowSecurity = false; + plansource->dependsOnRLS = false; plansource->gplan = NULL; plansource->is_oneshot = true; plansource->is_complete = false; @@ -276,8 +274,6 @@ CreateOneShotCachedPlan(Node *raw_parse_tree, plansource->generic_cost = -1; plansource->total_custom_cost = 0; plansource->num_custom_plans = 0; - plansource->planUserId = InvalidOid; - plansource->row_security_env = false; return plansource; } @@ -384,7 +380,11 @@ CompleteCachedPlan(CachedPlanSource *plansource, extract_query_dependencies((Node *) querytree_list, &plansource->relationOids, &plansource->invalItems, - &plansource->hasRowSecurity); + &plansource->dependsOnRLS); + + /* Update RLS info as well. */ + plansource->rewriteRoleId = GetUserId(); + plansource->rewriteRowSecurity = row_security; /* * Also save the current search_path in the query_context. (This @@ -416,8 +416,6 @@ CompleteCachedPlan(CachedPlanSource *plansource, plansource->cursor_options = cursor_options; plansource->fixed_result = fixed_result; plansource->resultDesc = PlanCacheComputeResultDesc(querytree_list); - plansource->planUserId = GetUserId(); - plansource->row_security_env = row_security; MemoryContextSwitchTo(oldcxt); @@ -583,12 +581,10 @@ RevalidateCachedQuery(CachedPlanSource *plansource) /* * If the query is currently valid, we should have a saved search_path --- * check to see if that matches the current environment. If not, we want - * to force replan. We should also have a valid planUserId. + * to force replan. */ if (plansource->is_valid) { - Assert(OidIsValid(plansource->planUserId)); - Assert(plansource->search_path != NULL); if (!OverrideSearchPathMatchesCurrent(plansource->search_path)) { @@ -600,29 +596,15 @@ RevalidateCachedQuery(CachedPlanSource *plansource) } /* - * If the plan has a possible RLS dependency, force a replan if either the - * role or the row_security setting has changed. + * If the query rewrite phase had a possible RLS dependency, we must redo + * it if either the role or the row_security setting has changed. */ - if (plansource->is_valid - && plansource->hasRowSecurity - && (plansource->planUserId != GetUserId() - || plansource->row_security_env != row_security)) + if (plansource->is_valid && plansource->dependsOnRLS && + (plansource->rewriteRoleId != GetUserId() || + plansource->rewriteRowSecurity != row_security)) 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. @@ -657,14 +639,6 @@ RevalidateCachedQuery(CachedPlanSource *plansource) plansource->search_path = NULL; /* - * The plan is invalid, possibly due to row security, so we need to reset - * row_security_env and planUserId as we're about to re-plan with the - * current settings. - */ - plansource->row_security_env = row_security; - plansource->planUserId = GetUserId(); - - /* * Free the query_context. We don't really expect MemoryContextDelete to * fail, but just in case, make sure the CachedPlanSource is left in a * reasonably sane state. (The generic plan won't get unlinked yet, but @@ -774,7 +748,11 @@ RevalidateCachedQuery(CachedPlanSource *plansource) extract_query_dependencies((Node *) qlist, &plansource->relationOids, &plansource->invalItems, - &plansource->hasRowSecurity); + &plansource->dependsOnRLS); + + /* Update RLS info as well. */ + plansource->rewriteRoleId = GetUserId(); + plansource->rewriteRowSecurity = row_security; /* * Also save the current search_path in the query_context. (This should @@ -832,6 +810,13 @@ CheckCachedPlan(CachedPlanSource *plansource) Assert(!plan->is_oneshot); /* + * If plan isn't valid for current role, we can't use it. + */ + if (plan->is_valid && plan->dependsOnRole && + plan->planRoleId != GetUserId()) + plan->is_valid = false; + + /* * If it appears valid, acquire locks and recheck; this is much the same * logic as in RevalidateCachedQuery, but for a plan. */ @@ -900,6 +885,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, List *plist; bool snapshot_set; bool spi_pushed; + bool is_transient; MemoryContext plan_context; MemoryContext oldcxt = CurrentMemoryContext; ListCell *lc; @@ -997,7 +983,28 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist, plan = (CachedPlan *) palloc(sizeof(CachedPlan)); plan->magic = CACHEDPLAN_MAGIC; plan->stmt_list = plist; - if (plan_list_is_transient(plist)) + + /* + * CachedPlan is dependent on role either if RLS affected the rewrite + * phase or if a role dependency was injected during planning. And it's + * transient if any plan is marked so. + */ + plan->planRoleId = GetUserId(); + plan->dependsOnRole = plansource->dependsOnRLS; + is_transient = false; + foreach(lc, plist) + { + PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); + + if (!IsA(plannedstmt, PlannedStmt)) + continue; /* Ignore utility statements */ + + if (plannedstmt->transientPlan) + is_transient = true; + if (plannedstmt->dependsOnRole) + plan->dependsOnRole = true; + } + if (is_transient) { Assert(TransactionIdIsNormal(TransactionXmin)); plan->saved_xmin = TransactionXmin; @@ -1010,20 +1017,6 @@ 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); @@ -1401,6 +1394,9 @@ CopyCachedPlan(CachedPlanSource *plansource) if (plansource->search_path) newsource->search_path = CopyOverrideSearchPath(plansource->search_path); newsource->query_context = querytree_context; + newsource->rewriteRoleId = plansource->rewriteRoleId; + newsource->rewriteRowSecurity = plansource->rewriteRowSecurity; + newsource->dependsOnRLS = plansource->dependsOnRLS; newsource->gplan = NULL; @@ -1416,14 +1412,6 @@ CopyCachedPlan(CachedPlanSource *plansource) newsource->total_custom_cost = plansource->total_custom_cost; newsource->num_custom_plans = plansource->num_custom_plans; - /* - * Copy over the user the query was planned as, and under what RLS - * environment. We will check during RevalidateCachedQuery() if the user - * or environment has changed and, if so, will force a re-plan. - */ - newsource->planUserId = plansource->planUserId; - newsource->row_security_env = plansource->row_security_env; - MemoryContextSwitchTo(oldcxt); return newsource; @@ -1668,28 +1656,6 @@ ScanQueryWalker(Node *node, bool *acquire) } /* - * plan_list_is_transient: check if any of the plans in the list are transient. - */ -static bool -plan_list_is_transient(List *stmt_list) -{ - ListCell *lc; - - foreach(lc, stmt_list) - { - PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc); - - if (!IsA(plannedstmt, PlannedStmt)) - continue; /* Ignore utility statements */ - - if (plannedstmt->transientPlan) - return true; - } - - return false; -} - -/* * PlanCacheComputeResultDesc: given a list of analyzed-and-rewritten Queries, * determine the result tupledesc it will produce. Returns NULL if the * execution will not return tuples. @@ -1888,40 +1854,6 @@ 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 |