mirror of
https://github.com/postgres/postgres.git
synced 2025-07-31 22:04:40 +03: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:
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
prv->next = nxt;
|
||||||
else
|
else
|
||||||
file_list = nxt;
|
file_list = nxt;
|
||||||
|
clear_external_function_hash(file_scanner->handle);
|
||||||
pg_dlclose(file_scanner->handle);
|
pg_dlclose(file_scanner->handle);
|
||||||
free((char *) file_scanner);
|
free((char *) file_scanner);
|
||||||
/* prv does not change */
|
/* prv does not change */
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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? */
|
* toastable datatype? */
|
||||||
} Oldstyle_fnextra;
|
} 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,
|
static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
|
||||||
bool ignore_security);
|
bool ignore_security);
|
||||||
static void fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);
|
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 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_oldstyle(PG_FUNCTION_ARGS);
|
||||||
static Datum fmgr_security_definer(PG_FUNCTION_ARGS);
|
static Datum fmgr_security_definer(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
@ -258,29 +276,44 @@ static void
|
|||||||
fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
|
fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
|
||||||
{
|
{
|
||||||
Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
|
Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
|
||||||
Datum prosrcattr,
|
CFuncHashTabEntry *hashentry;
|
||||||
probinattr;
|
|
||||||
char *prosrcstring,
|
|
||||||
*probinstring;
|
|
||||||
void *libraryhandle;
|
|
||||||
PGFunction user_fn;
|
PGFunction user_fn;
|
||||||
Pg_finfo_record *inforec;
|
Pg_finfo_record *inforec;
|
||||||
Oldstyle_fnextra *fnextra;
|
Oldstyle_fnextra *fnextra;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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) */
|
/* Get prosrc and probin strings (link symbol and library filename) */
|
||||||
prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,
|
prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,
|
||||||
Anum_pg_proc_prosrc, &isnull);
|
Anum_pg_proc_prosrc, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "null prosrc for function %u", functionId);
|
elog(ERROR, "null prosrc for function %u", functionId);
|
||||||
prosrcstring = DatumGetCString(DirectFunctionCall1(textout, prosrcattr));
|
prosrcstring = DatumGetCString(DirectFunctionCall1(textout,
|
||||||
|
prosrcattr));
|
||||||
|
|
||||||
probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
|
probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
|
||||||
Anum_pg_proc_probin, &isnull);
|
Anum_pg_proc_probin, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
elog(ERROR, "null probin for function %u", functionId);
|
elog(ERROR, "null probin for function %u", functionId);
|
||||||
probinstring = DatumGetCString(DirectFunctionCall1(textout, probinattr));
|
probinstring = DatumGetCString(DirectFunctionCall1(textout,
|
||||||
|
probinattr));
|
||||||
|
|
||||||
/* Look up the function itself */
|
/* Look up the function itself */
|
||||||
user_fn = load_external_function(probinstring, prosrcstring, true,
|
user_fn = load_external_function(probinstring, prosrcstring, true,
|
||||||
@ -289,6 +322,13 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
|
|||||||
/* Get the function information record (real or default) */
|
/* Get the function information record (real or default) */
|
||||||
inforec = fetch_finfo_record(libraryhandle, prosrcstring);
|
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)
|
switch (inforec->api_version)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
@ -315,9 +355,6 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
|
|||||||
inforec->api_version);
|
inforec->api_version);
|
||||||
break;
|
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
|
* Copy an FmgrInfo struct
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* Routines in fmgr.c
|
||||||
*/
|
*/
|
||||||
extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
|
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 fmgr_internal_function(const char *proname);
|
||||||
extern Oid get_fn_expr_rettype(FmgrInfo *flinfo);
|
extern Oid get_fn_expr_rettype(FmgrInfo *flinfo);
|
||||||
extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
|
extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
|
||||||
|
Reference in New Issue
Block a user