From fbe5a3fb73102c2cfec11aaaa4a67943f4474383 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Thu, 28 Jan 2016 14:05:36 -0500 Subject: 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. --- src/backend/optimizer/util/relnode.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'src/backend/optimizer/util/relnode.c') diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 7428c18af9f..420692f7a4d 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -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; } -- cgit v1.2.3