diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index ad9c911542d18a41b15c59f0e281b3db9593921e..d6d20fde9af6813c4cc8ec1465eee02ad8670d99 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -227,7 +227,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) FdwRoutine *fdwroutine; bool ok = false; - fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(onerel)); + fdwroutine = GetFdwRoutineForRelation(onerel, false); if (fdwroutine->AnalyzeForeignTable != NULL) ok = fdwroutine->AnalyzeForeignTable(onerel, diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index 6ebffadef198f07feb68db18cdbaacfae03058a9..63478cd12ad311f692b94cab86c0093ec7c2a348 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -160,7 +160,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) /* * Acquire function pointers from the FDW's handler, and init fdw_state. */ - fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(currentRelation)); + fdwroutine = GetFdwRoutineForRelation(currentRelation, true); scanstate->fdwroutine = fdwroutine; scanstate->fdw_state = NULL; diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index bfcc323924aaebe0e48a1a258929085f81fe0bca..2b75f73e08fb42b6793ed8e61cbac6c814e92b69 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -23,6 +23,8 @@ #include "lib/stringinfo.h" #include "miscadmin.h" #include "utils/builtins.h" +#include "utils/memutils.h" +#include "utils/rel.h" #include "utils/syscache.h" @@ -352,6 +354,50 @@ GetFdwRoutineByRelId(Oid relid) return GetFdwRoutine(fdwhandler); } +/* + * GetFdwRoutineForRelation - look up the handler of the foreign-data wrapper + * for the given foreign table, and retrieve its FdwRoutine struct. + * + * This function is preferred over GetFdwRoutineByRelId because it caches + * the data in the relcache entry, saving a number of catalog lookups. + * + * If makecopy is true then the returned data is freshly palloc'd in the + * caller's memory context. Otherwise, it's a pointer to the relcache data, + * which will be lost in any relcache reset --- so don't rely on it long. + */ +FdwRoutine * +GetFdwRoutineForRelation(Relation relation, bool makecopy) +{ + FdwRoutine *fdwroutine; + FdwRoutine *cfdwroutine; + + if (relation->rd_fdwroutine == NULL) + { + /* Get the info by consulting the catalogs and the FDW code */ + fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation)); + + /* Save the data for later reuse in CacheMemoryContext */ + cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext, + sizeof(FdwRoutine)); + memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine)); + relation->rd_fdwroutine = cfdwroutine; + + /* Give back the locally palloc'd copy regardless of makecopy */ + return fdwroutine; + } + + /* We have valid cached data --- does the caller want a copy? */ + if (makecopy) + { + fdwroutine = (FdwRoutine *) palloc(sizeof(FdwRoutine)); + memcpy(fdwroutine, relation->rd_fdwroutine, sizeof(FdwRoutine)); + return fdwroutine; + } + + /* Only a short-lived reference is needed, so just hand back cached copy */ + return relation->rd_fdwroutine; +} + /* * deflist_to_tuplestore - Helper function to convert DefElem list to diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 0545f958f67aee26d89891e1dfcc727a492d47b3..86d5bb71b0a0a3811a2d5fa2f0682b8964ff037d 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -410,9 +410,6 @@ set_foreign_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Mark rel with estimated output rows, width, etc */ set_foreign_size_estimates(root, rel); - /* Get FDW routine pointers for the rel */ - rel->fdwroutine = GetFdwRoutineByRelId(rte->relid); - /* Let FDW adjust the size estimates, if it can */ rel->fdwroutine->GetForeignRelSize(root, rel, rte->relid); } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index bff7aff593d89b5e2a6cbb2486b5c008fee3a3d5..954666ce04ce7b5fae849329ea1b67c34332878a 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -26,6 +26,7 @@ #include "access/xlog.h" #include "catalog/catalog.h" #include "catalog/heap.h" +#include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" @@ -67,6 +68,7 @@ static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index, * min_attr lowest valid AttrNumber * max_attr highest valid AttrNumber * indexlist list of IndexOptInfos for relation's indexes + * fdwroutine if it's a foreign table, the FDW function pointers * pages number of pages * tuples number of tuples * @@ -374,6 +376,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, rel->indexlist = indexinfos; + /* Grab the fdwroutine info using the relcache, while we have it */ + if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + rel->fdwroutine = GetFdwRoutineForRelation(relation, true); + else + rel->fdwroutine = NULL; + heap_close(relation, NoLock); /* diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index ba03dfcbb2df848a0f061fffa68b8b052f9c87f3..5b1d1e5b10a53d7647efbb24b40b91ca0d5195c3 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -1846,6 +1846,8 @@ RelationDestroyRelation(Relation relation) MemoryContextDelete(relation->rd_indexcxt); if (relation->rd_rulescxt) MemoryContextDelete(relation->rd_rulescxt); + if (relation->rd_fdwroutine) + pfree(relation->rd_fdwroutine); pfree(relation); } @@ -4410,7 +4412,7 @@ load_relcache_init_file(bool shared) * format is complex and subject to change). They must be rebuilt if * needed by RelationCacheInitializePhase3. This is not expected to * be a big performance hit since few system catalogs have such. Ditto - * for index expressions, predicates, and exclusion info. + * for index expressions, predicates, exclusion info, and FDW info. */ rel->rd_rules = NULL; rel->rd_rulescxt = NULL; @@ -4420,6 +4422,7 @@ load_relcache_init_file(bool shared) rel->rd_exclops = NULL; rel->rd_exclprocs = NULL; rel->rd_exclstrats = NULL; + rel->rd_fdwroutine = NULL; /* * Reset transient-state fields in the relcache entry diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index 13dcbfdf8c73828b35d130033a8c68038fa77b97..562d5412df7021b01c8c71769b40007c9ebd349c 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -96,5 +96,6 @@ typedef struct FdwRoutine /* Functions in foreign/foreign.c */ extern FdwRoutine *GetFdwRoutine(Oid fdwhandler); extern FdwRoutine *GetFdwRoutineByRelId(Oid relid); +extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy); #endif /* FDWAPI_H */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 06e1531e9a3385b99675c378d5834c1756a13150..a4daf772e57c8e1ad02a0ecb7bfa8c09b265f53b 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -165,6 +165,17 @@ typedef struct RelationData void *rd_amcache; /* available for use by index AM */ Oid *rd_indcollation; /* OIDs of index collations */ + /* + * foreign-table support + * + * rd_fdwroutine must point to a single memory chunk palloc'd in + * CacheMemoryContext. It will be freed and reset to NULL on a relcache + * reset. + */ + + /* use "struct" here to avoid needing to include fdwapi.h: */ + struct FdwRoutine *rd_fdwroutine; /* cached function pointers, or NULL */ + /* * Hack for CLUSTER, rewriting ALTER TABLE, etc: when writing a new * version of a table, we need to make any toast pointers inserted into it