]> granicus.if.org Git - postgresql/commitdiff
Add some additional core functions to support join pushdown for FDWs.
authorRobert Haas <rhaas@postgresql.org>
Thu, 4 Feb 2016 22:05:09 +0000 (17:05 -0500)
committerRobert Haas <rhaas@postgresql.org>
Thu, 4 Feb 2016 22:05:09 +0000 (17:05 -0500)
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

doc/src/sgml/fdwhandler.sgml
src/backend/foreign/foreign.c
src/include/foreign/fdwapi.h
src/include/foreign/foreign.h

index c6b60fa57982eb126c3b3ca948addd12b7a3b22a..a6945d35658a0d51bdc464a3d589696447d6d5b2 100644 (file)
@@ -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>
index 47c00af74f9c68e96bfc50aee8b03246441448a8..213217966cfb648de09af413916302ca55829143 100644 (file)
@@ -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.
@@ -240,8 +288,8 @@ find_user_mapping(Oid userid, Oid serverid)
 
        /* Not found for the specific user -- try PUBLIC */
        tp = SearchSysCache2(USERMAPPINGUSERSERVER,
-                                                        ObjectIdGetDatum(InvalidOid),
-                                                        ObjectIdGetDatum(serverid));
+                                                ObjectIdGetDatum(InvalidOid),
+                                                ObjectIdGetDatum(serverid));
 
        if (!HeapTupleIsValid(tp))
                ereport(ERROR,
@@ -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;
+}
index e16fbf34ec858354df07e48d133c1337f471e0a9..9fafab06e956f637bd7f8d4245254d714d850d80 100644 (file)
@@ -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 */
index d1359163e48eb5b17993258f34fa7fa1cf6555a6..71f8e55b0e95911aa44bb07d14f5c334d081084f 100644 (file)
@@ -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);