diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2004-01-19 02:06:42 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2004-01-19 02:06:42 +0000 |
commit | 5c625a9326e37093420044dcc70477cdbdd5f001 (patch) | |
tree | dc0a973d54a54c6e54a3b9656734398273ed8d31 /src | |
parent | 90d14654d6b5d564176f28d25c63d71f8578d6f0 (diff) | |
download | postgresql-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.c | 3 | ||||
-rw-r--r-- | src/backend/utils/fmgr/fmgr.c | 189 | ||||
-rw-r--r-- | src/include/fmgr.h | 3 |
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); |