From 1908abc4a37d397356c9cdf0fd31c33a86281d63 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 6 Mar 2013 23:47:38 -0500 Subject: [PATCH] Arrange to cache FdwRoutine structs in foreign tables' relcache entries. This saves several catalog lookups per reference. It's not all that exciting right now, because we'd managed to minimize the number of places that need to fetch the data; but the upcoming writable-foreign-tables patch needs this info in a lot more places. --- src/backend/commands/analyze.c | 2 +- src/backend/executor/nodeForeignscan.c | 2 +- src/backend/foreign/foreign.c | 46 ++++++++++++++++++++++++++ src/backend/optimizer/path/allpaths.c | 3 -- src/backend/optimizer/util/plancat.c | 8 +++++ src/backend/utils/cache/relcache.c | 5 ++- src/include/foreign/fdwapi.h | 1 + src/include/utils/rel.h | 11 ++++++ 8 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index ad9c911542..d6d20fde9a 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 6ebffadef1..63478cd12a 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 bfcc323924..2b75f73e08 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 0545f958f6..86d5bb71b0 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 bff7aff593..954666ce04 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 ba03dfcbb2..5b1d1e5b10 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 13dcbfdf8c..562d5412df 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 06e1531e9a..a4daf772e5 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 -- GitLab