aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execParallel.c14
-rw-r--r--src/backend/foreign/foreign.c38
-rw-r--r--src/backend/nodes/copyfuncs.c5
-rw-r--r--src/backend/nodes/outfuncs.c12
-rw-r--r--src/backend/nodes/readfuncs.c5
-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
-rw-r--r--src/backend/utils/cache/plancache.c178
-rw-r--r--src/include/foreign/foreign.h2
-rw-r--r--src/include/nodes/parsenodes.h4
-rw-r--r--src/include/nodes/plannodes.h9
-rw-r--r--src/include/nodes/relation.h17
-rw-r--r--src/include/utils/plancache.h9
16 files changed, 173 insertions, 262 deletions
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 6de90705e48..380d743f6ce 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -145,10 +145,12 @@ ExecSerializePlan(Plan *plan, EState *estate)
pstmt = makeNode(PlannedStmt);
pstmt->commandType = CMD_SELECT;
pstmt->queryId = 0;
- pstmt->hasReturning = 0;
- pstmt->hasModifyingCTE = 0;
- pstmt->canSetTag = 1;
- pstmt->transientPlan = 0;
+ pstmt->hasReturning = false;
+ pstmt->hasModifyingCTE = false;
+ pstmt->canSetTag = true;
+ pstmt->transientPlan = false;
+ pstmt->dependsOnRole = false;
+ pstmt->parallelModeNeeded = false;
pstmt->planTree = plan;
pstmt->rtable = estate->es_range_table;
pstmt->resultRelations = NIL;
@@ -156,11 +158,9 @@ ExecSerializePlan(Plan *plan, EState *estate)
pstmt->subplans = NIL;
pstmt->rewindPlanIDs = NULL;
pstmt->rowMarks = NIL;
- pstmt->nParamExec = estate->es_plannedstmt->nParamExec;
pstmt->relationOids = NIL;
pstmt->invalItems = NIL; /* workers can't replan anyway... */
- pstmt->hasRowSecurity = false;
- pstmt->hasForeignJoin = false;
+ pstmt->nParamExec = estate->es_plannedstmt->nParamExec;
/* Return serialized copy of our dummy PlannedStmt. */
return nodeToString(pstmt);
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 633b9832e54..66f98f1c7ec 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -31,7 +31,8 @@
extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
-static HeapTuple find_user_mapping(Oid userid, Oid serverid, bool missing_ok);
+static HeapTuple find_user_mapping(Oid userid, Oid serverid);
+
/*
* GetForeignDataWrapper - look up the foreign-data wrapper by OID.
@@ -223,7 +224,7 @@ GetUserMapping(Oid userid, Oid serverid)
bool isnull;
UserMapping *um;
- tp = find_user_mapping(userid, serverid, false);
+ tp = find_user_mapping(userid, serverid);
um = (UserMapping *) palloc(sizeof(UserMapping));
um->umid = HeapTupleGetOid(tp);
@@ -250,23 +251,14 @@ GetUserMapping(Oid userid, Oid serverid)
*
* If no mapping is found for the supplied user, we also look for
* PUBLIC mappings (userid == InvalidOid).
- *
- * If missing_ok is true, the function returns InvalidOid when it does not find
- * required user mapping. Otherwise, find_user_mapping() throws error if it
- * does not find required user mapping.
*/
Oid
-GetUserMappingId(Oid userid, Oid serverid, bool missing_ok)
+GetUserMappingId(Oid userid, Oid serverid)
{
HeapTuple tp;
Oid umid;
- tp = find_user_mapping(userid, serverid, missing_ok);
-
- Assert(missing_ok || tp);
-
- if (!tp && missing_ok)
- return InvalidOid;
+ tp = find_user_mapping(userid, serverid);
/* Extract the Oid */
umid = HeapTupleGetOid(tp);
@@ -276,19 +268,14 @@ GetUserMappingId(Oid userid, Oid serverid, bool missing_ok)
return umid;
}
-
/*
* find_user_mapping - Guts of GetUserMapping family.
*
* If no mapping is found for the supplied user, we also look for
* PUBLIC mappings (userid == InvalidOid).
- *
- * If missing_ok is true, the function returns NULL, if it does not find
- * the required user mapping. Otherwise, it throws error if it does not
- * find the required user mapping.
*/
static HeapTuple
-find_user_mapping(Oid userid, Oid serverid, bool missing_ok)
+find_user_mapping(Oid userid, Oid serverid)
{
HeapTuple tp;
@@ -305,15 +292,10 @@ find_user_mapping(Oid userid, Oid serverid, bool missing_ok)
ObjectIdGetDatum(serverid));
if (!HeapTupleIsValid(tp))
- {
- if (missing_ok)
- return NULL;
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("user mapping not found for \"%s\"",
- MappingUserName(userid))));
- }
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("user mapping not found for \"%s\"",
+ MappingUserName(userid))));
return tp;
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index d2786575d98..3244c76ddcc 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -85,6 +85,8 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_SCALAR_FIELD(hasModifyingCTE);
COPY_SCALAR_FIELD(canSetTag);
COPY_SCALAR_FIELD(transientPlan);
+ COPY_SCALAR_FIELD(dependsOnRole);
+ COPY_SCALAR_FIELD(parallelModeNeeded);
COPY_NODE_FIELD(planTree);
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(resultRelations);
@@ -95,9 +97,6 @@ _copyPlannedStmt(const PlannedStmt *from)
COPY_NODE_FIELD(relationOids);
COPY_NODE_FIELD(invalItems);
COPY_SCALAR_FIELD(nParamExec);
- COPY_SCALAR_FIELD(hasRowSecurity);
- COPY_SCALAR_FIELD(parallelModeNeeded);
- COPY_SCALAR_FIELD(hasForeignJoin);
return newnode;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c43ebe1a50b..e1966227379 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -261,6 +261,8 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_BOOL_FIELD(hasModifyingCTE);
WRITE_BOOL_FIELD(canSetTag);
WRITE_BOOL_FIELD(transientPlan);
+ WRITE_BOOL_FIELD(dependsOnRole);
+ WRITE_BOOL_FIELD(parallelModeNeeded);
WRITE_NODE_FIELD(planTree);
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(resultRelations);
@@ -271,9 +273,6 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node)
WRITE_NODE_FIELD(relationOids);
WRITE_NODE_FIELD(invalItems);
WRITE_INT_FIELD(nParamExec);
- WRITE_BOOL_FIELD(hasRowSecurity);
- WRITE_BOOL_FIELD(parallelModeNeeded);
- WRITE_BOOL_FIELD(hasForeignJoin);
}
/*
@@ -2014,11 +2013,11 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
WRITE_INT_FIELD(nParamExec);
WRITE_UINT_FIELD(lastPHId);
WRITE_UINT_FIELD(lastRowMarkId);
+ WRITE_INT_FIELD(lastPlanNodeId);
WRITE_BOOL_FIELD(transientPlan);
- WRITE_BOOL_FIELD(hasRowSecurity);
+ WRITE_BOOL_FIELD(dependsOnRole);
WRITE_BOOL_FIELD(parallelModeOK);
WRITE_BOOL_FIELD(parallelModeNeeded);
- WRITE_BOOL_FIELD(hasForeignJoin);
}
static void
@@ -2106,7 +2105,10 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_FLOAT_FIELD(allvisfrac, "%.6f");
WRITE_NODE_FIELD(subroot);
WRITE_NODE_FIELD(subplan_params);
+ WRITE_INT_FIELD(rel_parallel_workers);
WRITE_OID_FIELD(serverid);
+ WRITE_OID_FIELD(userid);
+ WRITE_BOOL_FIELD(useridiscurrent);
/* we don't try to print fdwroutine or fdw_private */
WRITE_NODE_FIELD(baserestrictinfo);
WRITE_NODE_FIELD(joininfo);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 491d3e08edc..94954dcc722 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1390,6 +1390,8 @@ _readPlannedStmt(void)
READ_BOOL_FIELD(hasModifyingCTE);
READ_BOOL_FIELD(canSetTag);
READ_BOOL_FIELD(transientPlan);
+ READ_BOOL_FIELD(dependsOnRole);
+ READ_BOOL_FIELD(parallelModeNeeded);
READ_NODE_FIELD(planTree);
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(resultRelations);
@@ -1400,9 +1402,6 @@ _readPlannedStmt(void)
READ_NODE_FIELD(relationOids);
READ_NODE_FIELD(invalItems);
READ_INT_FIELD(nParamExec);
- READ_BOOL_FIELD(hasRowSecurity);
- READ_BOOL_FIELD(parallelModeNeeded);
- READ_BOOL_FIELD(hasForeignJoin);
READ_DONE();
}
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;
+ }
}
/*
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
diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h
index f45873f4ee9..ad586e446a9 100644
--- a/src/include/foreign/foreign.h
+++ b/src/include/foreign/foreign.h
@@ -72,7 +72,7 @@ typedef struct ForeignTable
extern ForeignServer *GetForeignServer(Oid serverid);
extern ForeignServer *GetForeignServerByName(const char *name, bool missing_ok);
extern UserMapping *GetUserMapping(Oid userid, Oid serverid);
-extern Oid GetUserMappingId(Oid userid, Oid serverid, bool missing_ok);
+extern Oid GetUserMappingId(Oid userid, Oid serverid);
extern UserMapping *GetUserMappingById(Oid umid);
extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid);
extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d36d9c6d019..3773dd9c2ff 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -121,7 +121,7 @@ typedef struct Query
bool hasRecursive; /* WITH RECURSIVE was specified */
bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
- bool hasRowSecurity; /* row security applied? */
+ bool hasRowSecurity; /* rewriter has applied some RLS policy */
List *cteList; /* WITH list (of CommonTableExpr's) */
@@ -2823,7 +2823,7 @@ typedef enum VacuumOption
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock (autovacuum only) */
VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */
- VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
+ VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
} VacuumOption;
typedef struct VacuumStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index b375870e199..369179f2912 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -49,6 +49,10 @@ typedef struct PlannedStmt
bool transientPlan; /* redo plan when TransactionXmin changes? */
+ bool dependsOnRole; /* is plan specific to current role? */
+
+ bool parallelModeNeeded; /* parallel mode required to execute? */
+
struct Plan *planTree; /* tree of Plan nodes */
List *rtable; /* list of RangeTblEntry nodes */
@@ -69,11 +73,6 @@ typedef struct PlannedStmt
List *invalItems; /* other dependencies, as PlanInvalItems */
int nParamExec; /* number of PARAM_EXEC Params used */
-
- bool hasRowSecurity; /* row security applied? */
-
- bool parallelModeNeeded; /* parallel mode required to execute? */
- bool hasForeignJoin; /* Plan has a pushed down foreign join */
} PlannedStmt;
/* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index cd463535760..2be8908445b 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -121,13 +121,11 @@ typedef struct PlannerGlobal
bool transientPlan; /* redo plan when TransactionXmin changes? */
- bool hasRowSecurity; /* row security applied? */
+ bool dependsOnRole; /* is plan specific to current role? */
bool parallelModeOK; /* parallel mode potentially OK? */
bool parallelModeNeeded; /* parallel mode actually required? */
-
- bool hasForeignJoin; /* does have a pushed down foreign join */
} PlannerGlobal;
/* macro for fetching the Plan associated with a SubPlan node */
@@ -426,11 +424,12 @@ typedef struct PlannerInfo
* in just as for a baserel, except we don't bother with lateral_vars.
*
* If the relation is either a foreign table or a join of foreign tables that
- * all belong to the same foreign server and use the same user mapping, these
- * fields will be set:
+ * all belong to the same foreign server and are assigned to the same user to
+ * check access permissions as (cf checkAsUser), these fields will be set:
*
* serverid - OID of foreign server, if foreign table (else InvalidOid)
- * umid - OID of user mapping, if foreign table (else InvalidOid)
+ * userid - OID of user to check access as (InvalidOid means current user)
+ * useridiscurrent - we've assumed that userid equals current user
* fdwroutine - function hooks for FDW, if foreign table (else NULL)
* fdw_private - private state for FDW, if foreign table (else NULL)
*
@@ -528,8 +527,8 @@ typedef struct RelOptInfo
/* Information about foreign tables and foreign joins */
Oid serverid; /* identifies server for the table or join */
- Oid umid; /* identifies user mapping for the table or
- * join */
+ Oid userid; /* identifies user to check access as */
+ bool useridiscurrent; /* join is only valid for current user */
/* use "struct FdwRoutine" to avoid including fdwapi.h here */
struct FdwRoutine *fdwroutine;
void *fdw_private;
@@ -653,7 +652,7 @@ typedef struct ForeignKeyOptInfo
struct EquivalenceClass *eclass[INDEX_MAX_KEYS];
/* List of non-EC RestrictInfos matching each column's condition */
List *rinfos[INDEX_MAX_KEYS];
-} ForeignKeyOptInfo;
+} ForeignKeyOptInfo;
/*
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index 251f2d71862..938c4afc8be 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -93,8 +93,10 @@ typedef struct CachedPlanSource
List *invalItems; /* other dependencies, as PlanInvalItems */
struct OverrideSearchPath *search_path; /* search_path used for
* parsing and planning */
- Oid planUserId; /* User-id that the plan depends on */
MemoryContext query_context; /* context holding the above, or NULL */
+ Oid rewriteRoleId; /* Role ID we did rewriting for */
+ bool rewriteRowSecurity; /* row_security used during rewrite */
+ bool dependsOnRLS; /* is rewritten query specific to the above? */
/* If we have a generic plan, this is a reference-counted link to it: */
struct CachedPlan *gplan; /* generic plan, or NULL if not valid */
/* Some state flags: */
@@ -109,8 +111,6 @@ typedef struct CachedPlanSource
double generic_cost; /* cost of generic plan, or -1 if not known */
double total_custom_cost; /* total cost of custom plans so far */
int num_custom_plans; /* number of plans included in total */
- bool hasRowSecurity; /* planned with row security? */
- bool row_security_env; /* row security setting when planned */
} CachedPlanSource;
/*
@@ -131,11 +131,12 @@ typedef struct CachedPlan
bool is_oneshot; /* is it a "oneshot" plan? */
bool is_saved; /* is CachedPlan in a long-lived context? */
bool is_valid; /* is the stmt_list currently valid? */
+ Oid planRoleId; /* Role ID the plan was created for */
+ bool dependsOnRole; /* is plan specific to that role? */
TransactionId saved_xmin; /* if valid, replan when TransactionXmin
* changes from this value */
int generation; /* parent's generation number for this plan */
int refcount; /* count of live references to this struct */
- bool has_foreign_join; /* plan has pushed down a foreign join */
MemoryContext context; /* context containing this CachedPlan */
} CachedPlan;