mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-03-01 19:45:33 +08:00
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.
This commit is contained in:
parent
90d14654d6
commit
5c625a9326
@ -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 */
|
||||
|
@ -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));
|
||||
/*
|
||||
* 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;
|
||||
|
||||
probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
|
||||
Anum_pg_proc_probin, &isnull);
|
||||
if (isnull)
|
||||
elog(ERROR, "null probin for function %u", functionId);
|
||||
probinstring = DatumGetCString(DirectFunctionCall1(textout, probinattr));
|
||||
/* 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));
|
||||
|
||||
/* Look up the function itself */
|
||||
user_fn = load_external_function(probinstring, prosrcstring, true,
|
||||
&libraryhandle);
|
||||
probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
|
||||
Anum_pg_proc_probin, &isnull);
|
||||
if (isnull)
|
||||
elog(ERROR, "null probin for function %u", functionId);
|
||||
probinstring = DatumGetCString(DirectFunctionCall1(textout,
|
||||
probinattr));
|
||||
|
||||
/* Get the function information record (real or default) */
|
||||
inforec = fetch_finfo_record(libraryhandle, prosrcstring);
|
||||
/* 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
|
||||
*
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user