diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/joinpath.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 11 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 25 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 20 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 82 |
5 files changed, 70 insertions, 72 deletions
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 9d06fb2637e..cc7384f7e5e 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -213,8 +213,8 @@ add_paths_to_joinrel(PlannerInfo *root, /* * 5. If inner and outer relations are foreign tables (or joins) belonging - * to the same server and using the same user mapping, give the FDW a - * chance to push down joins. + * to the same server and assigned to the same user to check access + * permissions as, give the FDW a chance to push down joins. */ if (joinrel->fdwroutine && joinrel->fdwroutine->GetForeignJoinPaths) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index cebfe197346..54d601fc47d 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -3247,13 +3247,12 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, scan_plan->fs_relids = best_path->path.parent->relids; /* - * If a join between foreign relations was pushed down, remember it. The - * push-down safety of the join depends upon the server and user mapping - * being same. That can change between planning and execution time, in - * which case the plan should be invalidated. + * If this is a foreign join, and to make it valid to push down we had to + * assume that the current user is the same as some user explicitly named + * in the query, mark the finished plan as depending on the current user. */ - if (scan_relid == 0) - root->glob->hasForeignJoin = true; + if (rel->useridiscurrent) + root->glob->dependsOnRole = true; /* * Replace any outer-relation variables with nestloop params in the qual, diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index f484fb91c11..b2656283251 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -219,8 +219,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) glob->lastRowMarkId = 0; glob->lastPlanNodeId = 0; glob->transientPlan = false; - glob->hasRowSecurity = false; - glob->hasForeignJoin = false; + glob->dependsOnRole = false; /* * Assess whether it's feasible to use parallel mode for this query. We @@ -405,6 +404,8 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) result->hasModifyingCTE = parse->hasModifyingCTE; result->canSetTag = parse->canSetTag; result->transientPlan = glob->transientPlan; + result->dependsOnRole = glob->dependsOnRole; + result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; result->resultRelations = glob->resultRelations; @@ -415,9 +416,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) result->relationOids = glob->relationOids; result->invalItems = glob->invalItems; result->nParamExec = glob->nParamExec; - result->hasRowSecurity = glob->hasRowSecurity; - result->parallelModeNeeded = glob->parallelModeNeeded; - result->hasForeignJoin = glob->hasForeignJoin; return result; } @@ -1628,8 +1626,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, * This may add new security barrier subquery RTEs to the rangetable. */ expand_security_quals(root, tlist); - if (parse->hasRowSecurity) - root->glob->hasRowSecurity = true; /* * We are now done hacking up the query's targetlist. Most of the @@ -1960,7 +1956,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, * If the current_rel belongs to a single FDW, so does the final_rel. */ final_rel->serverid = current_rel->serverid; - final_rel->umid = current_rel->umid; + final_rel->userid = current_rel->userid; + final_rel->useridiscurrent = current_rel->useridiscurrent; final_rel->fdwroutine = current_rel->fdwroutine; /* @@ -3337,7 +3334,8 @@ create_grouping_paths(PlannerInfo *root, * If the input rel belongs to a single FDW, so does the grouped rel. */ grouped_rel->serverid = input_rel->serverid; - grouped_rel->umid = input_rel->umid; + grouped_rel->userid = input_rel->userid; + grouped_rel->useridiscurrent = input_rel->useridiscurrent; grouped_rel->fdwroutine = input_rel->fdwroutine; /* @@ -3891,7 +3889,8 @@ create_window_paths(PlannerInfo *root, * If the input rel belongs to a single FDW, so does the window rel. */ window_rel->serverid = input_rel->serverid; - window_rel->umid = input_rel->umid; + window_rel->userid = input_rel->userid; + window_rel->useridiscurrent = input_rel->useridiscurrent; window_rel->fdwroutine = input_rel->fdwroutine; /* @@ -4071,7 +4070,8 @@ create_distinct_paths(PlannerInfo *root, * If the input rel belongs to a single FDW, so does the distinct_rel. */ distinct_rel->serverid = input_rel->serverid; - distinct_rel->umid = input_rel->umid; + distinct_rel->userid = input_rel->userid; + distinct_rel->useridiscurrent = input_rel->useridiscurrent; distinct_rel->fdwroutine = input_rel->fdwroutine; /* Estimate number of distinct rows there will be */ @@ -4279,7 +4279,8 @@ create_ordered_paths(PlannerInfo *root, * If the input rel belongs to a single FDW, so does the ordered_rel. */ ordered_rel->serverid = input_rel->serverid; - ordered_rel->umid = input_rel->umid; + ordered_rel->userid = input_rel->userid; + ordered_rel->useridiscurrent = input_rel->useridiscurrent; ordered_rel->fdwroutine = input_rel->fdwroutine; foreach(lc, input_rel->pathlist) diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index ffff6db2490..6ea46c46812 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -2432,9 +2432,10 @@ record_plan_function_dependency(PlannerInfo *root, Oid funcid) /* * extract_query_dependencies - * Given a not-yet-planned query or queries (i.e. a Query node or list - * of Query nodes), extract dependencies just as set_plan_references - * would do. + * Given a rewritten, but not yet planned, query or queries + * (i.e. a Query node or list of Query nodes), extract dependencies + * just as set_plan_references would do. Also detect whether any + * rewrite steps were affected by RLS. * * This is needed by plancache.c to handle invalidation of cached unplanned * queries. @@ -2453,7 +2454,8 @@ extract_query_dependencies(Node *query, glob.type = T_PlannerGlobal; glob.relationOids = NIL; glob.invalItems = NIL; - glob.hasRowSecurity = false; + /* Hack: we use glob.dependsOnRole to collect hasRowSecurity flags */ + glob.dependsOnRole = false; MemSet(&root, 0, sizeof(root)); root.type = T_PlannerInfo; @@ -2463,7 +2465,7 @@ extract_query_dependencies(Node *query, *relationOids = glob.relationOids; *invalItems = glob.invalItems; - *hasRowSecurity = glob.hasRowSecurity; + *hasRowSecurity = glob.dependsOnRole; } static bool @@ -2479,10 +2481,6 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context) Query *query = (Query *) node; ListCell *lc; - /* Collect row security information */ - if (query->hasRowSecurity) - context->glob->hasRowSecurity = true; - if (query->commandType == CMD_UTILITY) { /* @@ -2494,6 +2492,10 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context) return false; } + /* Remember if any Query has RLS quals applied by rewriter */ + if (query->hasRowSecurity) + context->glob->dependsOnRole = true; + /* Collect relation OIDs in this Query's rtable */ foreach(lc, query->rtable) { diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index a0a284b901b..806600ed107 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -15,8 +15,6 @@ #include "postgres.h" #include "miscadmin.h" -#include "catalog/pg_class.h" -#include "foreign/foreign.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" @@ -107,7 +105,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) rel->consider_startup = (root->tuple_fraction > 0); rel->consider_param_startup = false; /* might get changed later */ rel->consider_parallel = false; /* might get changed later */ - rel->rel_parallel_workers = -1; /* set up in GetRelationInfo */ rel->reltarget = create_empty_pathtarget(); rel->pathlist = NIL; rel->ppilist = NIL; @@ -129,8 +126,10 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) rel->allvisfrac = 0; rel->subroot = NULL; rel->subplan_params = NIL; + rel->rel_parallel_workers = -1; /* set up in GetRelationInfo */ rel->serverid = InvalidOid; - rel->umid = InvalidOid; + rel->userid = rte->checkAsUser; + rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; rel->baserestrictinfo = NIL; @@ -170,30 +169,6 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) break; } - /* For foreign tables get the user mapping */ - if (rte->relkind == RELKIND_FOREIGN_TABLE) - { - /* - * This should match what ExecCheckRTEPerms() does. - * - * Note that if the plan ends up depending on the user OID in any way - * - e.g. if it depends on the computed user mapping OID - we must - * ensure that it gets invalidated in the case of a user OID change. - * See RevalidateCachedQuery and more generally the hasForeignJoin - * flags in PlannerGlobal and PlannedStmt. - * - * It's possible, and not necessarily an error, for rel->umid to be - * InvalidOid even though rel->serverid is set. That just means there - * is a server with no user mapping. - */ - Oid userid; - - userid = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId(); - rel->umid = GetUserMappingId(userid, rel->serverid, true); - } - else - rel->umid = InvalidOid; - /* Save the finished struct in the query's simple_rel_array */ root->simple_rel_array[relid] = rel; @@ -423,8 +398,10 @@ build_join_rel(PlannerInfo *root, joinrel->allvisfrac = 0; joinrel->subroot = NULL; joinrel->subplan_params = NIL; + joinrel->rel_parallel_workers = -1; joinrel->serverid = InvalidOid; - joinrel->umid = InvalidOid; + joinrel->userid = InvalidOid; + joinrel->useridiscurrent = false; joinrel->fdwroutine = NULL; joinrel->fdw_private = NULL; joinrel->baserestrictinfo = NIL; @@ -435,24 +412,43 @@ build_join_rel(PlannerInfo *root, /* * Set up foreign-join fields if outer and inner relation are foreign - * tables (or joins) belonging to the same server and using the same user - * mapping. - * - * Otherwise those fields are left invalid, so FDW API will not be called - * for the join relation. + * tables (or joins) belonging to the same server and assigned to the same + * user to check access permissions as. In addition to an exact match of + * userid, we allow the case where one side has zero userid (implying + * current user) and the other side has explicit userid that happens to + * equal the current user; but in that case, pushdown of the join is only + * valid for the current user. The useridiscurrent field records whether + * we had to make such an assumption for this join or any sub-join. * - * For FDWs like file_fdw, which ignore user mapping, the user mapping id - * associated with the joining relation may be invalid. A valid serverid - * distinguishes between a pushed down join with no user mapping and a - * join which can not be pushed down because of user mapping mismatch. + * Otherwise these fields are left invalid, so GetForeignJoinPaths will + * not be called for the join relation. */ if (OidIsValid(outer_rel->serverid) && - inner_rel->serverid == outer_rel->serverid && - inner_rel->umid == outer_rel->umid) + inner_rel->serverid == outer_rel->serverid) { - joinrel->serverid = outer_rel->serverid; - joinrel->umid = outer_rel->umid; - joinrel->fdwroutine = outer_rel->fdwroutine; + if (inner_rel->userid == outer_rel->userid) + { + joinrel->serverid = outer_rel->serverid; + joinrel->userid = outer_rel->userid; + joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent; + joinrel->fdwroutine = outer_rel->fdwroutine; + } + else if (!OidIsValid(inner_rel->userid) && + outer_rel->userid == GetUserId()) + { + joinrel->serverid = outer_rel->serverid; + joinrel->userid = outer_rel->userid; + joinrel->useridiscurrent = true; + joinrel->fdwroutine = outer_rel->fdwroutine; + } + else if (!OidIsValid(outer_rel->userid) && + inner_rel->userid == GetUserId()) + { + joinrel->serverid = outer_rel->serverid; + joinrel->userid = inner_rel->userid; + joinrel->useridiscurrent = true; + joinrel->fdwroutine = outer_rel->fdwroutine; + } } /* |