See <xref linkend="fdw-planning"> for additional information.
</para>
+ <para>
+<programlisting>
+void
+GetExistingLocalJoinPath(RelOptInfo *joinrel)
+</programlisting>
+ The function returns copy of a local join path, which can be converted
+ into an alternative local join plan, which may be useful when
+ implementing a <literal>RecheckForeignScan</> method. The function
+ searches for a parallel-safe, unparameterized path in the
+ <literal>pathlist</> of given <literal>joinrel</>. If it does not find
+ such a path, it returns NULL, in which case a foreign data wrapper may
+ build the local path by itself or may choose not to create access paths
+ for that join.
+ </para>
+
</sect2>
<sect2 id="fdw-callbacks-update">
can be executed and the resulting tuple can be stored in the slot.
This plan need not be efficient since no base table will return more
than one row; for example, it may implement all joins as nested loops.
+ <literal>GetExistingLocalJoinPath</> may be used to search existing paths
+ for a suitable local join path, which can be used as the alternative
+ local join plan.
</para>
</sect2>
<para>
<programlisting>
+UserMapping *
+GetUserMappingById(Oid umid);
+</programlisting>
+
+ This function returns the <structname>UserMapping</structname> object for
+ the given user mapping OID. The OID of a user mapping for a foreign scan
+ is available in the <structname>RelOptInfo</structname>.
+ If there is no mapping for the OID, this function will throw an error.
+ A <structname>UserMapping</structname> object contains properties of the
+ user mapping (see <filename>foreign/foreign.h</filename> for details).
+ </para>
+
+ <para>
+<programlisting>
List *
GetForeignColumnOptions(Oid relid, AttrNumber attnum);
</programlisting>
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));
+ um->umid = umid;
+
+ /* 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.
/* Not found for the specific user -- try PUBLIC */
tp = SearchSysCache2(USERMAPPINGUSERSERVER,
- ObjectIdGetDatum(InvalidOid),
- ObjectIdGetDatum(serverid));
+ ObjectIdGetDatum(InvalidOid),
+ ObjectIdGetDatum(serverid));
if (!HeapTupleIsValid(tp))
ereport(ERROR,
errmsg("server \"%s\" does not exist", servername)));
return oid;
}
+
+/*
+ * Get a copy of an existing local path for a given join relation.
+ *
+ * This function is usually helpful to obtain an alternate local path for EPQ
+ * checks.
+ *
+ * Right now, this function only supports unparameterized foreign joins, so we
+ * only search for unparameterized path in the given list of paths. Since we
+ * are searching for a path which can be used to construct an alternative local
+ * plan for a foreign join, we look for only MergeJoin, HashJoin or NestLoop
+ * paths.
+ *
+ * If the inner or outer subpath of the chosen path is a ForeignScan, we
+ * replace it with its outer subpath. For this reason, and also because the
+ * planner might free the original path later, the path returned by this
+ * function is a shallow copy of the original. There's no need to copy
+ * the substructure, so we don't.
+ *
+ * Since the plan created using this path will presumably only be used to
+ * execute EPQ checks, efficiency of the path is not a concern. But since the
+ * list passed is expected to be from RelOptInfo, it's anyway sorted by total
+ * cost and hence we are likely to choose the most efficient path, which is
+ * all for the best.
+ */
+extern Path *
+GetExistingLocalJoinPath(RelOptInfo *joinrel)
+{
+ ListCell *lc;
+
+ Assert(joinrel->reloptkind == RELOPT_JOINREL);
+
+ foreach(lc, joinrel->pathlist)
+ {
+ Path *path = (Path *) lfirst(lc);
+ JoinPath *joinpath = NULL;
+
+ /* Skip parameterised or non-parallel-safe paths. */
+ if (path->param_info != NULL || !path->parallel_safe)
+ continue;
+
+ switch (path->pathtype)
+ {
+ case T_HashJoin:
+ {
+ HashPath *hash_path = makeNode(HashPath);
+
+ memcpy(hash_path, path, sizeof(HashPath));
+ joinpath = (JoinPath *) hash_path;
+ }
+ break;
+
+ case T_NestLoop:
+ {
+ NestPath *nest_path = makeNode(NestPath);
+
+ memcpy(nest_path, path, sizeof(NestPath));
+ joinpath = (JoinPath *) nest_path;
+ }
+ break;
+
+ case T_MergeJoin:
+ {
+ MergePath *merge_path = makeNode(MergePath);
+
+ memcpy(merge_path, path, sizeof(MergePath));
+ joinpath = (JoinPath *) merge_path;
+ }
+ break;
+
+ default:
+
+ /*
+ * Just skip anything else. We don't know if corresponding
+ * plan would build the output row from whole-row references
+ * of base relations and execute the EPQ checks.
+ */
+ break;
+ }
+
+ /* This path isn't good for us, check next. */
+ if (!joinpath)
+ continue;
+
+ /*
+ * If either inner or outer path is a ForeignPath corresponding to a
+ * pushed down join, replace it with the fdw_outerpath, so that we
+ * maintain path for EPQ checks built entirely of local join
+ * strategies.
+ */
+ if (IsA(joinpath->outerjoinpath, ForeignPath))
+ {
+ ForeignPath *foreign_path;
+
+ foreign_path = (ForeignPath *) joinpath->outerjoinpath;
+ if (foreign_path->path.parent->reloptkind == RELOPT_JOINREL)
+ joinpath->outerjoinpath = foreign_path->fdw_outerpath;
+ }
+
+ if (IsA(joinpath->innerjoinpath, ForeignPath))
+ {
+ ForeignPath *foreign_path;
+
+ foreign_path = (ForeignPath *) joinpath->innerjoinpath;
+ if (foreign_path->path.parent->reloptkind == RELOPT_JOINREL)
+ joinpath->innerjoinpath = foreign_path->fdw_outerpath;
+ }
+
+ return (Path *) joinpath;
+ }
+ return NULL;
+}