mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-12 18:34:36 +08:00
Add some additional core functions to support join pushdown for FDWs.
GetExistingLocalJoinPath() is useful for handling EvalPlanQual rechecks properly, and GetUserMappingById() is needed to make sure you're using the right credentials. Shigeru Hanada, Etsuro Fujita, Ashutosh Bapat, Robert Haas
This commit is contained in:
parent
c1772ad922
commit
a104a017fc
@ -341,6 +341,21 @@ GetForeignJoinPaths (PlannerInfo *root,
|
||||
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">
|
||||
@ -794,6 +809,9 @@ RecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot);
|
||||
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>
|
||||
|
||||
@ -1069,6 +1087,20 @@ GetForeignTable(Oid relid);
|
||||
|
||||
<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>
|
||||
|
@ -160,6 +160,54 @@ 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));
|
||||
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.
|
||||
@ -732,3 +780,115 @@ get_foreign_server_oid(const char *servername, bool missing_ok)
|
||||
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;
|
||||
}
|
||||
|
@ -202,5 +202,6 @@ extern FdwRoutine *GetFdwRoutineByRelId(Oid relid);
|
||||
extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy);
|
||||
extern bool IsImportableForeignTable(const char *tablename,
|
||||
ImportForeignSchemaStmt *stmt);
|
||||
extern Path *GetExistingLocalJoinPath(RelOptInfo *joinrel);
|
||||
|
||||
#endif /* FDWAPI_H */
|
||||
|
@ -73,6 +73,7 @@ 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);
|
||||
|
Loading…
Reference in New Issue
Block a user