diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index 1533a6b..d961f1b 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -992,6 +992,32 @@ GetForeignTable(Oid relid); +UserMapping * +GetUserMappingById(Oid umid); + + + This function returns a UserMapping object for + the given user mapping OID. The OID of a user mapping is available in + RelOptInfo for a foreign scan. + (If there is no mapping for the OID, it will throw an error.) + A UserMapping object contains properties of the + user mapping (see foreign/foreign.h for details). + + + + +ForeignTable * +GetForeignTable(Oid relid); + + + This function returns a ForeignTable object for + the foreign table with the given OID. A + ForeignTable object contains properties of the + foreign table (see foreign/foreign.h for details). + + + + List * GetForeignColumnOptions(Oid relid, AttrNumber attnum); diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index 763ee7c..85ef743 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. @@ -159,6 +160,53 @@ GetForeignServerByName(const char *srvname, bool missing_ok) return GetForeignServer(serverid); } +/* + * GetUserMappingById - look up the user mapping by its OID. + */ +UserMapping * +GetUserMappingById(Oid umid) +{ + Datum datum; + HeapTuple tp; + bool isnull; + UserMapping *um; + + tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for user mapping %u", umid); + + um = (UserMapping *) palloc(sizeof(UserMapping)); + + /* Extract the umuser */ + datum = SysCacheGetAttr(USERMAPPINGOID, + tp, + Anum_pg_user_mapping_umuser, + &isnull); + Assert(!isnull); + um->userid = DatumGetObjectId(datum); + + /* Extract the umserver */ + datum = SysCacheGetAttr(USERMAPPINGOID, + tp, + Anum_pg_user_mapping_umserver, + &isnull); + Assert(!isnull); + um->serverid = DatumGetObjectId(datum); + + /* Extract the umoptions */ + datum = SysCacheGetAttr(USERMAPPINGOID, + tp, + Anum_pg_user_mapping_umoptions, + &isnull); + if (isnull) + um->options = NIL; + else + um->options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return um; +} /* * GetUserMapping - look up the user mapping. @@ -174,23 +222,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; @@ -211,6 +243,61 @@ GetUserMapping(Oid userid, Oid serverid) return um; } +/* + * 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)) + return 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. diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 1b61fd9..a90f4f4 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -124,6 +124,7 @@ bool enable_nestloop = true; bool enable_material = true; bool enable_mergejoin = true; bool enable_hashjoin = true; +bool enable_foreignjoin = true; typedef struct { diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index a35c881..b632b94 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -256,7 +256,8 @@ add_paths_to_joinrel(PlannerInfo *root, * 5. If inner and outer relations are foreign tables (or joins) belonging * to the same server, give the FDW a chance to push down joins. */ - if (joinrel->fdwroutine && + if (enable_foreignjoin && + joinrel->fdwroutine && joinrel->fdwroutine->GetForeignJoinPaths) joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel, outerrel, innerrel, diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 9442e5f..8aa2e67 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" @@ -384,12 +385,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 68a93a1..284da6d 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -123,6 +123,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; @@ -387,6 +388,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; @@ -397,12 +399,21 @@ 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. + * TODO: It should suffice to just check the umid. If umid of both the + * relations is same, it implies same serverid and same user mapping both. */ 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/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index fda0fb9..459f793 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -864,6 +864,15 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, { + {"enable_foreignjoin", PGC_USERSET, QUERY_TUNING_METHOD, + gettext_noop("Allows the planner to push join between two foreign relations to the foreign server."), + NULL + }, + &enable_foreignjoin, + true, + NULL, NULL, NULL + }, + { {"geqo", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("Enables genetic query optimization."), gettext_noop("This algorithm attempts to do planning without " diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index c820e09..cd1a3cb 100644 --- a/src/include/foreign/foreign.h +++ b/src/include/foreign/foreign.h @@ -71,6 +71,8 @@ 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 UserMapping *GetUserMappingById(Oid umid); 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 6cf2e24..416ce99 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -484,6 +484,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; diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index 25a7303..6f9fd37 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -66,6 +66,7 @@ extern bool enable_nestloop; extern bool enable_material; extern bool enable_mergejoin; extern bool enable_hashjoin; +extern bool enable_foreignjoin; extern int constraint_exclusion; extern double clamp_row_est(double nrows);