Arrange to cache FdwRoutine structs in foreign tables' relcache entries.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 7 Mar 2013 04:47:38 +0000 (23:47 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 7 Mar 2013 04:48:09 +0000 (23:48 -0500)
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
src/backend/executor/nodeForeignscan.c
src/backend/foreign/foreign.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/util/plancat.c
src/backend/utils/cache/relcache.c
src/include/foreign/fdwapi.h
src/include/utils/rel.h

index ad9c911542d18a41b15c59f0e281b3db9593921e..d6d20fde9af6813c4cc8ec1465eee02ad8670d99 100644 (file)
@@ -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,
index 6ebffadef198f07feb68db18cdbaacfae03058a9..63478cd12ad311f692b94cab86c0093ec7c2a348 100644 (file)
@@ -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;
 
index bfcc323924aaebe0e48a1a258929085f81fe0bca..2b75f73e08fb42b6793ed8e61cbac6c814e92b69 100644 (file)
@@ -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
index 0545f958f67aee26d89891e1dfcc727a492d47b3..86d5bb71b0a0a3811a2d5fa2f0682b8964ff037d 100644 (file)
@@ -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);
 }
index bff7aff593d89b5e2a6cbb2486b5c008fee3a3d5..954666ce04ce7b5fae849329ea1b67c34332878a 100644 (file)
@@ -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);
 
        /*
index ba03dfcbb2df848a0f061fffa68b8b052f9c87f3..5b1d1e5b10a53d7647efbb24b40b91ca0d5195c3 100644 (file)
@@ -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
index 13dcbfdf8c73828b35d130033a8c68038fa77b97..562d5412df7021b01c8c71769b40007c9ebd349c 100644 (file)
@@ -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 */
index 06e1531e9a3385b99675c378d5834c1756a13150..a4daf772e57c8e1ad02a0ecb7bfa8c09b265f53b 100644 (file)
@@ -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