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);