Skip to content

Commit 5c625a9

Browse files
committed
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.
1 parent 90d1465 commit 5c625a9

File tree

3 files changed

+165
-30
lines changed

3 files changed

+165
-30
lines changed

src/backend/utils/fmgr/dfmgr.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.68 2004/01/07 18:56:29 neilc Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.69 2004/01/19 02:06:41 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -213,6 +213,7 @@ load_file(char *filename)
213213
prv->next = nxt;
214214
else
215215
file_list = nxt;
216+
clear_external_function_hash(file_scanner->handle);
216217
pg_dlclose(file_scanner->handle);
217218
free((char *) file_scanner);
218219
/* prv does not change */

src/backend/utils/fmgr/fmgr.c

Lines changed: 161 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.79 2004/01/07 18:56:29 neilc Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.80 2004/01/19 02:06:41 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -57,11 +57,29 @@ typedef struct
5757
* toastable datatype? */
5858
} Oldstyle_fnextra;
5959

60+
/*
61+
* Hashtable for fast lookup of external C functions
62+
*/
63+
typedef struct
64+
{
65+
/* fn_oid is the hash key and so must be first! */
66+
Oid fn_oid; /* OID of an external C function */
67+
TransactionId fn_xmin; /* for checking up-to-dateness */
68+
CommandId fn_cmin;
69+
PGFunction user_fn; /* the function's address */
70+
Pg_finfo_record *inforec; /* address of its info record */
71+
} CFuncHashTabEntry;
72+
73+
static HTAB *CFuncHash = NULL;
74+
6075

6176
static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
6277
bool ignore_security);
6378
static void fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);
6479
static void fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);
80+
static CFuncHashTabEntry *lookup_C_func(HeapTuple procedureTuple);
81+
static void record_C_func(HeapTuple procedureTuple,
82+
PGFunction user_fn, Pg_finfo_record *inforec);
6583
static Datum fmgr_oldstyle(PG_FUNCTION_ARGS);
6684
static Datum fmgr_security_definer(PG_FUNCTION_ARGS);
6785

@@ -258,36 +276,58 @@ static void
258276
fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
259277
{
260278
Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
261-
Datum prosrcattr,
262-
probinattr;
263-
char *prosrcstring,
264-
*probinstring;
265-
void *libraryhandle;
279+
CFuncHashTabEntry *hashentry;
266280
PGFunction user_fn;
267281
Pg_finfo_record *inforec;
268282
Oldstyle_fnextra *fnextra;
269283
bool isnull;
270284
int i;
271285

272-
/* Get prosrc and probin strings (link symbol and library filename) */
273-
prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,
274-
Anum_pg_proc_prosrc, &isnull);
275-
if (isnull)
276-
elog(ERROR, "null prosrc for function %u", functionId);
277-
prosrcstring = DatumGetCString(DirectFunctionCall1(textout, prosrcattr));
278-
279-
probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
280-
Anum_pg_proc_probin, &isnull);
281-
if (isnull)
282-
elog(ERROR, "null probin for function %u", functionId);
283-
probinstring = DatumGetCString(DirectFunctionCall1(textout, probinattr));
284-
285-
/* Look up the function itself */
286-
user_fn = load_external_function(probinstring, prosrcstring, true,
287-
&libraryhandle);
288-
289-
/* Get the function information record (real or default) */
290-
inforec = fetch_finfo_record(libraryhandle, prosrcstring);
286+
/*
287+
* See if we have the function address cached already
288+
*/
289+
hashentry = lookup_C_func(procedureTuple);
290+
if (hashentry)
291+
{
292+
user_fn = hashentry->user_fn;
293+
inforec = hashentry->inforec;
294+
}
295+
else
296+
{
297+
Datum prosrcattr,
298+
probinattr;
299+
char *prosrcstring,
300+
*probinstring;
301+
void *libraryhandle;
302+
303+
/* Get prosrc and probin strings (link symbol and library filename) */
304+
prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple,
305+
Anum_pg_proc_prosrc, &isnull);
306+
if (isnull)
307+
elog(ERROR, "null prosrc for function %u", functionId);
308+
prosrcstring = DatumGetCString(DirectFunctionCall1(textout,
309+
prosrcattr));
310+
311+
probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
312+
Anum_pg_proc_probin, &isnull);
313+
if (isnull)
314+
elog(ERROR, "null probin for function %u", functionId);
315+
probinstring = DatumGetCString(DirectFunctionCall1(textout,
316+
probinattr));
317+
318+
/* Look up the function itself */
319+
user_fn = load_external_function(probinstring, prosrcstring, true,
320+
&libraryhandle);
321+
322+
/* Get the function information record (real or default) */
323+
inforec = fetch_finfo_record(libraryhandle, prosrcstring);
324+
325+
/* Cache the addresses for later calls */
326+
record_C_func(procedureTuple, user_fn, inforec);
327+
328+
pfree(prosrcstring);
329+
pfree(probinstring);
330+
}
291331

292332
switch (inforec->api_version)
293333
{
@@ -315,9 +355,6 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
315355
inforec->api_version);
316356
break;
317357
}
318-
319-
pfree(prosrcstring);
320-
pfree(probinstring);
321358
}
322359

323360
/*
@@ -416,6 +453,102 @@ fetch_finfo_record(void *filehandle, char *funcname)
416453
}
417454

418455

456+
/*-------------------------------------------------------------------------
457+
* Routines for caching lookup information for external C functions.
458+
*
459+
* The routines in dfmgr.c are relatively slow, so we try to avoid running
460+
* them more than once per external function per session. We use a hash table
461+
* with the function OID as the lookup key.
462+
*-------------------------------------------------------------------------
463+
*/
464+
465+
/*
466+
* lookup_C_func: try to find a C function in the hash table
467+
*
468+
* If an entry exists and is up to date, return it; else return NULL
469+
*/
470+
static CFuncHashTabEntry *
471+
lookup_C_func(HeapTuple procedureTuple)
472+
{
473+
Oid fn_oid = HeapTupleGetOid(procedureTuple);
474+
CFuncHashTabEntry *entry;
475+
476+
if (CFuncHash == NULL)
477+
return NULL; /* no table yet */
478+
entry = (CFuncHashTabEntry *)
479+
hash_search(CFuncHash,
480+
&fn_oid,
481+
HASH_FIND,
482+
NULL);
483+
if (entry == NULL)
484+
return NULL; /* no such entry */
485+
if (entry->fn_xmin == HeapTupleHeaderGetXmin(procedureTuple->t_data) &&
486+
entry->fn_cmin == HeapTupleHeaderGetCmin(procedureTuple->t_data))
487+
return entry; /* OK */
488+
return NULL; /* entry is out of date */
489+
}
490+
491+
/*
492+
* record_C_func: enter (or update) info about a C function in the hash table
493+
*/
494+
static void
495+
record_C_func(HeapTuple procedureTuple,
496+
PGFunction user_fn, Pg_finfo_record *inforec)
497+
{
498+
Oid fn_oid = HeapTupleGetOid(procedureTuple);
499+
CFuncHashTabEntry *entry;
500+
bool found;
501+
502+
/* Create the hash table if it doesn't exist yet */
503+
if (CFuncHash == NULL)
504+
{
505+
HASHCTL hash_ctl;
506+
507+
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
508+
hash_ctl.keysize = sizeof(Oid);
509+
hash_ctl.entrysize = sizeof(CFuncHashTabEntry);
510+
hash_ctl.hash = tag_hash;
511+
CFuncHash = hash_create("CFuncHash",
512+
100,
513+
&hash_ctl,
514+
HASH_ELEM | HASH_FUNCTION);
515+
if (CFuncHash == NULL)
516+
ereport(ERROR,
517+
(errcode(ERRCODE_OUT_OF_MEMORY),
518+
errmsg("out of memory")));
519+
}
520+
521+
entry = (CFuncHashTabEntry *)
522+
hash_search(CFuncHash,
523+
&fn_oid,
524+
HASH_ENTER,
525+
&found);
526+
if (entry == NULL)
527+
ereport(ERROR,
528+
(errcode(ERRCODE_OUT_OF_MEMORY),
529+
errmsg("out of memory")));
530+
/* OID is already filled in */
531+
entry->fn_xmin = HeapTupleHeaderGetXmin(procedureTuple->t_data);
532+
entry->fn_cmin = HeapTupleHeaderGetCmin(procedureTuple->t_data);
533+
entry->user_fn = user_fn;
534+
entry->inforec = inforec;
535+
}
536+
537+
/*
538+
* clear_external_function_hash: remove entries for a library being closed
539+
*
540+
* Presently we just zap the entire hash table, but later it might be worth
541+
* the effort to remove only the entries associated with the given handle.
542+
*/
543+
void
544+
clear_external_function_hash(void *filehandle)
545+
{
546+
if (CFuncHash)
547+
hash_destroy(CFuncHash);
548+
CFuncHash = NULL;
549+
}
550+
551+
419552
/*
420553
* Copy an FmgrInfo struct
421554
*

src/include/fmgr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
1212
* Portions Copyright (c) 1994, Regents of the University of California
1313
*
14-
* $PostgreSQL: pgsql/src/include/fmgr.h,v 1.32 2003/11/29 22:40:53 pgsql Exp $
14+
* $PostgreSQL: pgsql/src/include/fmgr.h,v 1.33 2004/01/19 02:06:42 tgl Exp $
1515
*
1616
*-------------------------------------------------------------------------
1717
*/
@@ -377,6 +377,7 @@ extern Datum OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2,
377377
* Routines in fmgr.c
378378
*/
379379
extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
380+
extern void clear_external_function_hash(void *filehandle);
380381
extern Oid fmgr_internal_function(const char *proname);
381382
extern Oid get_fn_expr_rettype(FmgrInfo *flinfo);
382383
extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy