diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index dc2d890..77287fa 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -341,6 +341,20 @@ GetForeignJoinPaths (PlannerInfo *root, See for additional information. + + +void +GetPathForEPQRecheck(RelOptInfo *joinrel) + + The function returns copy of a local join path, which can be converted + into alternative local join plan, which in turn can be used by + RecheckForeignScan method. The function searches for a + suitable path in the pathlist of given joinrel. + If it does not find a suitable 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. + + @@ -794,6 +808,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. + GetPathForEPQRecheck may be used to search existing paths + for a suitable local join path, which can be converted into the alternative + local join plan. @@ -1022,6 +1039,20 @@ 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). + + + + List * GetForeignColumnOptions(Oid relid, AttrNumber attnum); diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index 47c00af..f728177 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -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,98 @@ get_foreign_server_oid(const char *servername, bool missing_ok) errmsg("server \"%s\" does not exist", servername))); return oid; } + +/* + * Get a copy of a local path for EPQ checks. + * + * Right now, we support only unparameterized foreign joins, so we only search + * for unparameterized path in the given list of paths. Since we are searching + * for an alternate local path for a foreign join, look for only MergeJoin, + * HashJoin or NestLoop paths. + * + * Since we will need to replace any foreign paths for join with their alternate + * paths, we need make a copy of the local path chosen. Make a shallow copy of + * the join path, because the planner might free the original structure after a + * future add_path(). We don't need to copy the substructure, though; that won't + * get freed. + * + * Since the plan created using this path will be used to execute the + * 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 suits our + * requirement. + */ +extern Path * +GetPathForEPQRecheck(RelOptInfo *joinrel) +{ + ListCell *lc; + + foreach(lc, joinrel->pathlist) + { + Path *path = (Path *) lfirst(lc); + JoinPath *joinpath; + + if (path->param_info == NULL) + { + 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; + } + + /* + * 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; +} diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index db73233..fb190b6 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -188,5 +188,6 @@ extern FdwRoutine *GetFdwRoutineByRelId(Oid relid); extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy); extern bool IsImportableForeignTable(const char *tablename, ImportForeignSchemaStmt *stmt); +extern Path *GetPathForEPQRecheck(RelOptInfo *joinrel); #endif /* FDWAPI_H */ diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index d135916..71f8e55 100644 --- a/src/include/foreign/foreign.h +++ b/src/include/foreign/foreign.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);