diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index 763ee7c..407dbf0 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -31,6 +31,7 @@ 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); /* * GetForeignDataWrapper - look up the foreign-data wrapper by OID. @@ -174,23 +175,7 @@ GetUserMapping(Oid userid, Oid serverid) bool isnull; UserMapping *um; - tp = SearchSysCache2(USERMAPPINGUSERSERVER, - ObjectIdGetDatum(userid), - ObjectIdGetDatum(serverid)); - - if (!HeapTupleIsValid(tp)) - { - /* Not found for the specific user -- try PUBLIC */ - tp = SearchSysCache2(USERMAPPINGUSERSERVER, - ObjectIdGetDatum(InvalidOid), - ObjectIdGetDatum(serverid)); - } - - if (!HeapTupleIsValid(tp)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("user mapping not found for \"%s\"", - MappingUserName(userid)))); + tp = find_user_mapping(userid, serverid); um = (UserMapping *) palloc(sizeof(UserMapping)); um->userid = userid; @@ -213,6 +198,62 @@ GetUserMapping(Oid userid, Oid serverid) /* + * GetUserMappingId - look up the user mapping, and return its OID + * + * If no mapping is found for the supplied user, we also look for + * PUBLIC mappings (userid == InvalidOid). + */ +Oid +GetUserMappingId(Oid userid, Oid serverid) +{ + HeapTuple tp; + Oid umid; + + tp = find_user_mapping(userid, serverid); + + /* Extract the Oid */ + umid = HeapTupleGetOid(tp); + + ReleaseSysCache(tp); + + 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). + */ +static HeapTuple +find_user_mapping(Oid userid, Oid serverid) +{ + HeapTuple tp; + + tp = SearchSysCache2(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(userid), + ObjectIdGetDatum(serverid)); + + if (!HeapTupleIsValid(tp)) + { + /* Not found for the specific user -- try PUBLIC */ + tp = SearchSysCache2(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(InvalidOid), + ObjectIdGetDatum(serverid)); + } + + if (!HeapTupleIsValid(tp)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("user mapping not found for \"%s\"", + MappingUserName(userid)))); + + return tp; +} + + +/* * GetForeignTable - look up the foreign table definition by relation oid. */ ForeignTable * diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index b04dc2e..6afbcea 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -28,6 +28,7 @@ #include "catalog/dependency.h" #include "catalog/heap.h" #include "foreign/fdwapi.h" +#include "foreign/foreign.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" @@ -383,12 +384,20 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, /* Grab foreign-table info using the relcache, while we have it */ if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) { + RangeTblEntry *rte; + Oid userid; + rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation)); rel->fdwroutine = GetFdwRoutineForRelation(relation, true); + + rte = planner_rt_fetch(rel->relid, root); + userid = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId(); + rel->umid = GetUserMappingId(userid, rel->serverid); } else { rel->serverid = InvalidOid; + rel->umid = InvalidOid; rel->fdwroutine = NULL; } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 1d635cd..f8abab4 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -122,6 +122,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; @@ -385,6 +386,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; @@ -395,12 +397,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 && + OidIsValid(outer_rel->umid) && + inner_rel->umid == outer_rel->umid) { joinrel->serverid = outer_rel->serverid; + joinrel->umid = outer_rel->umid; joinrel->fdwroutine = outer_rel->fdwroutine; } diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index c820e09..3e450d6 100644 --- a/src/include/foreign/foreign.h +++ b/src/include/foreign/foreign.h @@ -71,6 +71,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); extern ForeignDataWrapper *GetForeignDataWrapper(Oid fdwid); extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name, bool missing_ok); diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 279051e..2d6c313 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -469,6 +469,7 @@ 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 */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; void *fdw_private;