1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

Only try to push down foreign joins if the user mapping OIDs match.

Previously, the foreign join pushdown infrastructure left the question
of security entirely up to individual FDWs, but it would be easy for
a foreign data wrapper to inadvertently open up subtle security holes
that way.  So, make it the core code's job to determine which user
mapping OID is relevant, and don't attempt join pushdown unless it's
the same for all relevant relations.

Per a suggestion from Tom Lane.  Shigeru Hanada and Ashutosh Bapat,
reviewed by Etsuro Fujita and KaiGai Kohei, with some further
changes by me.
This commit is contained in:
Robert Haas
2016-01-28 14:05:36 -05:00
parent 2f6b041f76
commit fbe5a3fb73
13 changed files with 179 additions and 20 deletions

View File

@ -2151,6 +2151,15 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
/* Likewise, copy the relids that are represented by this foreign scan */
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 (scan_relid == 0)
root->glob->hasForeignJoin = true;
/*
* Replace any outer-relation variables with nestloop params in the qual,
* fdw_exprs and fdw_recheck_quals expressions. We do this last so that

View File

@ -200,6 +200,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->lastPlanNodeId = 0;
glob->transientPlan = false;
glob->hasRowSecurity = false;
glob->hasForeignJoin = false;
/*
* Assess whether it's feasible to use parallel mode for this query. We
@ -346,6 +347,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->nParamExec = glob->nParamExec;
result->hasRowSecurity = glob->hasRowSecurity;
result->parallelModeNeeded = glob->parallelModeNeeded;
result->hasForeignJoin = glob->hasForeignJoin;
return result;
}

View File

@ -14,6 +14,9 @@
*/
#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"
@ -127,6 +130,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->subroot = NULL;
rel->subplan_params = NIL;
rel->serverid = InvalidOid;
rel->umid = InvalidOid;
rel->fdwroutine = NULL;
rel->fdw_private = NULL;
rel->baserestrictinfo = NIL;
@ -166,6 +170,26 @@ 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.
*/
Oid userid;
userid = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId();
rel->umid = GetUserMappingId(userid, rel->serverid);
}
else
rel->umid = InvalidOid;
/* Save the finished struct in the query's simple_rel_array */
root->simple_rel_array[relid] = rel;
@ -398,6 +422,7 @@ build_join_rel(PlannerInfo *root,
joinrel->subroot = NULL;
joinrel->subplan_params = NIL;
joinrel->serverid = InvalidOid;
joinrel->umid = InvalidOid;
joinrel->fdwroutine = NULL;
joinrel->fdw_private = NULL;
joinrel->baserestrictinfo = NIL;
@ -408,12 +433,19 @@ 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.
* 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.
*/
if (OidIsValid(outer_rel->serverid) &&
inner_rel->serverid == outer_rel->serverid)
inner_rel->serverid == outer_rel->serverid &&
inner_rel->umid == outer_rel->umid)
{
Assert(OidIsValid(outer_rel->umid));
joinrel->serverid = outer_rel->serverid;
joinrel->umid = outer_rel->umid;
joinrel->fdwroutine = outer_rel->fdwroutine;
}