|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * 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 $ |
12 | 12 | *
|
13 | 13 | *-------------------------------------------------------------------------
|
14 | 14 | */
|
@@ -57,11 +57,29 @@ typedef struct
|
57 | 57 | * toastable datatype? */
|
58 | 58 | }Oldstyle_fnextra;
|
59 | 59 |
|
| 60 | +/* |
| 61 | + * Hashtable for fast lookup of external C functions |
| 62 | + */ |
| 63 | +typedefstruct |
| 64 | +{ |
| 65 | +/* fn_oid is the hash key and so must be first! */ |
| 66 | +Oidfn_oid;/* OID of an external C function */ |
| 67 | +TransactionIdfn_xmin;/* for checking up-to-dateness */ |
| 68 | +CommandIdfn_cmin; |
| 69 | +PGFunctionuser_fn;/* the function's address */ |
| 70 | +Pg_finfo_record*inforec;/* address of its info record */ |
| 71 | +}CFuncHashTabEntry; |
| 72 | + |
| 73 | +staticHTAB*CFuncHash=NULL; |
| 74 | + |
60 | 75 |
|
61 | 76 | staticvoidfmgr_info_cxt_security(OidfunctionId,FmgrInfo*finfo,MemoryContextmcxt,
|
62 | 77 | boolignore_security);
|
63 | 78 | staticvoidfmgr_info_C_lang(OidfunctionId,FmgrInfo*finfo,HeapTupleprocedureTuple);
|
64 | 79 | staticvoidfmgr_info_other_lang(OidfunctionId,FmgrInfo*finfo,HeapTupleprocedureTuple);
|
| 80 | +staticCFuncHashTabEntry*lookup_C_func(HeapTupleprocedureTuple); |
| 81 | +staticvoidrecord_C_func(HeapTupleprocedureTuple, |
| 82 | +PGFunctionuser_fn,Pg_finfo_record*inforec); |
65 | 83 | staticDatumfmgr_oldstyle(PG_FUNCTION_ARGS);
|
66 | 84 | staticDatumfmgr_security_definer(PG_FUNCTION_ARGS);
|
67 | 85 |
|
@@ -258,36 +276,58 @@ static void
|
258 | 276 | fmgr_info_C_lang(OidfunctionId,FmgrInfo*finfo,HeapTupleprocedureTuple)
|
259 | 277 | {
|
260 | 278 | Form_pg_procprocedureStruct= (Form_pg_proc)GETSTRUCT(procedureTuple);
|
261 |
| -Datumprosrcattr, |
262 |
| -probinattr; |
263 |
| -char*prosrcstring, |
264 |
| -*probinstring; |
265 |
| -void*libraryhandle; |
| 279 | +CFuncHashTabEntry*hashentry; |
266 | 280 | PGFunctionuser_fn;
|
267 | 281 | Pg_finfo_record*inforec;
|
268 | 282 | Oldstyle_fnextra*fnextra;
|
269 | 283 | boolisnull;
|
270 | 284 | inti;
|
271 | 285 |
|
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 | +Datumprosrcattr, |
| 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 | +} |
291 | 331 |
|
292 | 332 | switch (inforec->api_version)
|
293 | 333 | {
|
@@ -315,9 +355,6 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
|
315 | 355 | inforec->api_version);
|
316 | 356 | break;
|
317 | 357 | }
|
318 |
| - |
319 |
| -pfree(prosrcstring); |
320 |
| -pfree(probinstring); |
321 | 358 | }
|
322 | 359 |
|
323 | 360 | /*
|
@@ -416,6 +453,102 @@ fetch_finfo_record(void *filehandle, char *funcname)
|
416 | 453 | }
|
417 | 454 |
|
418 | 455 |
|
| 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 | +staticCFuncHashTabEntry* |
| 471 | +lookup_C_func(HeapTupleprocedureTuple) |
| 472 | +{ |
| 473 | +Oidfn_oid=HeapTupleGetOid(procedureTuple); |
| 474 | +CFuncHashTabEntry*entry; |
| 475 | + |
| 476 | +if (CFuncHash==NULL) |
| 477 | +returnNULL;/* no table yet */ |
| 478 | +entry= (CFuncHashTabEntry*) |
| 479 | +hash_search(CFuncHash, |
| 480 | +&fn_oid, |
| 481 | +HASH_FIND, |
| 482 | +NULL); |
| 483 | +if (entry==NULL) |
| 484 | +returnNULL;/* no such entry */ |
| 485 | +if (entry->fn_xmin==HeapTupleHeaderGetXmin(procedureTuple->t_data)&& |
| 486 | +entry->fn_cmin==HeapTupleHeaderGetCmin(procedureTuple->t_data)) |
| 487 | +returnentry;/* OK */ |
| 488 | +returnNULL;/* 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 | +staticvoid |
| 495 | +record_C_func(HeapTupleprocedureTuple, |
| 496 | +PGFunctionuser_fn,Pg_finfo_record*inforec) |
| 497 | +{ |
| 498 | +Oidfn_oid=HeapTupleGetOid(procedureTuple); |
| 499 | +CFuncHashTabEntry*entry; |
| 500 | +boolfound; |
| 501 | + |
| 502 | +/* Create the hash table if it doesn't exist yet */ |
| 503 | +if (CFuncHash==NULL) |
| 504 | +{ |
| 505 | +HASHCTLhash_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 | + |
419 | 552 | /*
|
420 | 553 | * Copy an FmgrInfo struct
|
421 | 554 | *
|
|