Add a hash table to cache lookups of 'C'-language functions (that is,
authorTom Lane
Mon, 19 Jan 2004 02:06:42 +0000 (02:06 +0000)
committerTom Lane
Mon, 19 Jan 2004 02:06:42 +0000 (02:06 +0000)
dynamically loaded C functions).  Some limited testing suggests that
this puts the lookup speed for external functions just about on par
with built-in functions.  Per discussion with Eric Ridge.

src/backend/utils/fmgr/dfmgr.c
src/backend/utils/fmgr/fmgr.c
src/include/fmgr.h

index 6c690f7b380062554b3aeeb916be28b788189e1f..3a66da5dc7326fffd0fe5e447e7790ec231f6b69 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.68 2004/01/07 18:56:29 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.69 2004/01/19 02:06:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -213,6 +213,7 @@ load_file(char *filename)
                prv->next = nxt;
            else
                file_list = nxt;
+           clear_external_function_hash(file_scanner->handle);
            pg_dlclose(file_scanner->handle);
            free((char *) file_scanner);
            /* prv does not change */
index 82f84bdd4e2aa7ccc5c141a9023a7f77991e9425..f4b7860fab2dc2051133bdcd792be99829d5f3c8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.79 2004/01/07 18:56:29 neilc Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.80 2004/01/19 02:06:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,11 +57,29 @@ typedef struct
                                                 * toastable datatype? */
 } Oldstyle_fnextra;
 
+/*
+ * Hashtable for fast lookup of external C functions
+ */
+typedef struct
+{
+   /* fn_oid is the hash key and so must be first! */
+   Oid         fn_oid;         /* OID of an external C function */
+   TransactionId fn_xmin;      /* for checking up-to-dateness */
+   CommandId   fn_cmin;
+   PGFunction  user_fn;        /* the function's address */
+   Pg_finfo_record *inforec;   /* address of its info record */
+} CFuncHashTabEntry;
+
+static HTAB *CFuncHash = NULL;
+
 
 static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
                       bool ignore_security);
 static void fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);
 static void fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);
+static CFuncHashTabEntry *lookup_C_func(HeapTuple procedureTuple);
+static void record_C_func(HeapTuple procedureTuple,
+                         PGFunction user_fn, Pg_finfo_record *inforec);
 static Datum fmgr_oldstyle(PG_FUNCTION_ARGS);
 static Datum fmgr_security_definer(PG_FUNCTION_ARGS);
 
@@ -258,36 +276,58 @@ static void
 fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
 {
    Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
-   Datum       prosrcattr,
-               probinattr;
-   char       *prosrcstring,
-              *probinstring;
-   void       *libraryhandle;
+   CFuncHashTabEntry *hashentry;
    PGFunction  user_fn;
    Pg_finfo_record *inforec;
    Oldstyle_fnextra *fnextra;
    bool        isnull;
    int         i;
 
-   /* Get prosrc and probin strings (link symbol and library filename) */
-   prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,
-                                Anum_pg_proc_prosrc, &isnull);
-   if (isnull)
-       elog(ERROR, "null prosrc for function %u", functionId);
-   prosrcstring = DatumGetCString(DirectFunctionCall1(textout, prosrcattr));
-
-   probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
-                                Anum_pg_proc_probin, &isnull);
-   if (isnull)
-       elog(ERROR, "null probin for function %u", functionId);
-   probinstring = DatumGetCString(DirectFunctionCall1(textout, probinattr));
-
-   /* Look up the function itself */
-   user_fn = load_external_function(probinstring, prosrcstring, true,
-                                    &libraryhandle);
-
-   /* Get the function information record (real or default) */
-   inforec = fetch_finfo_record(libraryhandle, prosrcstring);
+   /*
+    * See if we have the function address cached already
+    */
+   hashentry = lookup_C_func(procedureTuple);
+   if (hashentry)
+   {
+       user_fn = hashentry->user_fn;
+       inforec = hashentry->inforec;
+   }
+   else
+   {
+       Datum       prosrcattr,
+                   probinattr;
+       char       *prosrcstring,
+                  *probinstring;
+       void       *libraryhandle;
+
+       /* Get prosrc and probin strings (link symbol and library filename) */
+       prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,
+                                    Anum_pg_proc_prosrc, &isnull);
+       if (isnull)
+           elog(ERROR, "null prosrc for function %u", functionId);
+       prosrcstring = DatumGetCString(DirectFunctionCall1(textout,
+                                                          prosrcattr));
+
+       probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
+                                    Anum_pg_proc_probin, &isnull);
+       if (isnull)
+           elog(ERROR, "null probin for function %u", functionId);
+       probinstring = DatumGetCString(DirectFunctionCall1(textout,
+                                                          probinattr));
+
+       /* Look up the function itself */
+       user_fn = load_external_function(probinstring, prosrcstring, true,
+                                        &libraryhandle);
+
+       /* Get the function information record (real or default) */
+       inforec = fetch_finfo_record(libraryhandle, prosrcstring);
+
+       /* Cache the addresses for later calls */
+       record_C_func(procedureTuple, user_fn, inforec);
+
+       pfree(prosrcstring);
+       pfree(probinstring);
+   }
 
    switch (inforec->api_version)
    {
@@ -315,9 +355,6 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
                 inforec->api_version);
            break;
    }
-
-   pfree(prosrcstring);
-   pfree(probinstring);
 }
 
 /*
@@ -416,6 +453,102 @@ fetch_finfo_record(void *filehandle, char *funcname)
 }
 
 
+/*-------------------------------------------------------------------------
+ *     Routines for caching lookup information for external C functions.
+ *
+ * The routines in dfmgr.c are relatively slow, so we try to avoid running
+ * them more than once per external function per session.  We use a hash table
+ * with the function OID as the lookup key.
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * lookup_C_func: try to find a C function in the hash table
+ *
+ * If an entry exists and is up to date, return it; else return NULL
+ */
+static CFuncHashTabEntry *
+lookup_C_func(HeapTuple procedureTuple)
+{
+   Oid     fn_oid = HeapTupleGetOid(procedureTuple);
+   CFuncHashTabEntry *entry;
+
+   if (CFuncHash == NULL)
+       return NULL;            /* no table yet */
+   entry = (CFuncHashTabEntry *)
+       hash_search(CFuncHash,
+                   &fn_oid,
+                   HASH_FIND,
+                   NULL);
+   if (entry == NULL)
+       return NULL;            /* no such entry */
+   if (entry->fn_xmin == HeapTupleHeaderGetXmin(procedureTuple->t_data) &&
+       entry->fn_cmin == HeapTupleHeaderGetCmin(procedureTuple->t_data))
+       return entry;           /* OK */
+   return NULL;                /* entry is out of date */
+}
+
+/*
+ * record_C_func: enter (or update) info about a C function in the hash table
+ */
+static void
+record_C_func(HeapTuple procedureTuple,
+             PGFunction user_fn, Pg_finfo_record *inforec)
+{
+   Oid     fn_oid = HeapTupleGetOid(procedureTuple);
+   CFuncHashTabEntry *entry;
+   bool    found;
+
+   /* Create the hash table if it doesn't exist yet */
+   if (CFuncHash == NULL)
+   {
+       HASHCTL     hash_ctl;
+
+       MemSet(&hash_ctl, 0, sizeof(hash_ctl));
+       hash_ctl.keysize = sizeof(Oid);
+       hash_ctl.entrysize = sizeof(CFuncHashTabEntry);
+       hash_ctl.hash = tag_hash;
+       CFuncHash = hash_create("CFuncHash",
+                               100,
+                               &hash_ctl,
+                               HASH_ELEM | HASH_FUNCTION);
+       if (CFuncHash == NULL)
+           ereport(ERROR,
+                   (errcode(ERRCODE_OUT_OF_MEMORY),
+                    errmsg("out of memory")));
+   }
+
+   entry = (CFuncHashTabEntry *)
+       hash_search(CFuncHash,
+                   &fn_oid,
+                   HASH_ENTER,
+                   &found);
+   if (entry == NULL)
+       ereport(ERROR,
+               (errcode(ERRCODE_OUT_OF_MEMORY),
+                errmsg("out of memory")));
+   /* OID is already filled in */
+   entry->fn_xmin = HeapTupleHeaderGetXmin(procedureTuple->t_data);
+   entry->fn_cmin = HeapTupleHeaderGetCmin(procedureTuple->t_data);
+   entry->user_fn = user_fn;
+   entry->inforec = inforec;
+}
+
+/*
+ * clear_external_function_hash: remove entries for a library being closed
+ *
+ * Presently we just zap the entire hash table, but later it might be worth
+ * the effort to remove only the entries associated with the given handle.
+ */
+void
+clear_external_function_hash(void *filehandle)
+{
+   if (CFuncHash)
+       hash_destroy(CFuncHash);
+   CFuncHash = NULL;
+}
+
+
 /*
  * Copy an FmgrInfo struct
  *
index 1e3b02e60ebb17444a43e27bbb53b3b07c1f86b5..6ed389e8f95acc8aa742bf2e7b40e43b2964e8b7 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.32 2003/11/29 22:40:53 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.33 2004/01/19 02:06:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -377,6 +377,7 @@ extern Datum OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2,
  * Routines in fmgr.c
  */
 extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
+extern void clear_external_function_hash(void *filehandle);
 extern Oid fmgr_internal_function(const char *proname);
 extern Oid get_fn_expr_rettype(FmgrInfo *flinfo);
 extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);