summaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/joinpath.c4
-rw-r--r--src/backend/optimizer/plan/createplan.c11
-rw-r--r--src/backend/optimizer/plan/planner.c25
-rw-r--r--src/backend/optimizer/plan/setrefs.c20
-rw-r--r--src/backend/optimizer/util/relnode.c82
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;
+ }
}
/*