aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-01-19 02:06:42 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-01-19 02:06:42 +0000
commit5c625a9326e37093420044dcc70477cdbdd5f001 (patch)
treedc0a973d54a54c6e54a3b9656734398273ed8d31 /src
parent90d14654d6b5d564176f28d25c63d71f8578d6f0 (diff)
downloadpostgresql-5c625a9326e37093420044dcc70477cdbdd5f001.tar.gz
postgresql-5c625a9326e37093420044dcc70477cdbdd5f001.zip
Add a hash table to cache lookups of 'C'-language functions (that is,
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/fmgr/dfmgr.c3
-rw-r--r--src/backend/utils/fmgr/fmgr.c189
-rw-r--r--src/include/fmgr.h3
3 files changed, 165 insertions, 30 deletions
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index 6c690f7b380..3a66da5dc73 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -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 */
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 82f84bdd4e2..f4b7860fab2 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -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
*
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 1e3b02e60eb..6ed389e8f95 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -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);