fmgr.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,224 行 · 第 1/4 页
C
2,224 行
/*------------------------------------------------------------------------- * * fmgr.c * The Postgres function manager. * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.113 2008/01/03 21:23:15 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "access/tuptoaster.h"#include "catalog/pg_language.h"#include "catalog/pg_proc.h"#include "executor/functions.h"#include "miscadmin.h"#include "parser/parse_expr.h"#include "utils/builtins.h"#include "utils/fmgrtab.h"#include "utils/guc.h"#include "utils/lsyscache.h"#include "utils/syscache.h"/* * Declaration for old-style function pointer type. This is now used only * in fmgr_oldstyle() and is no longer exported. * * The m68k SVR4 ABI defines that pointers are returned in %a0 instead of * %d0. So if a function pointer is declared to return a pointer, the * compiler may look only into %a0, but if the called function was declared * to return an integer type, it puts its value only into %d0. So the * caller doesn't pick up the correct return value. The solution is to * declare the function pointer to return int, so the compiler picks up the * return value from %d0. (Functions returning pointers put their value * *additionally* into %d0 for compatibility.) The price is that there are * some warnings about int->pointer conversions ... which we can suppress * with suitably ugly casts in fmgr_oldstyle(). */#if (defined(__mc68000__) || (defined(__m68k__))) && defined(__ELF__)typedef int32 (*func_ptr) ();#elsetypedef char *(*func_ptr) ();#endif/* * For an oldstyle function, fn_extra points to a record like this: */typedef struct{ func_ptr func; /* Address of the oldstyle function */ bool arg_toastable[FUNC_MAX_ARGS]; /* is n'th arg of a 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 */ ItemPointerData fn_tid; PGFunction user_fn; /* the function's address */ const 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, const Pg_finfo_record *inforec);static Datum fmgr_oldstyle(PG_FUNCTION_ARGS);static Datum fmgr_security_definer(PG_FUNCTION_ARGS);/* * Lookup routines for builtin-function table. We can search by either Oid * or name, but search by Oid is much faster. */static const FmgrBuiltin *fmgr_isbuiltin(Oid id){ int low = 0; int high = fmgr_nbuiltins - 1; /* * Loop invariant: low is the first index that could contain target entry, * and high is the last index that could contain it. */ while (low <= high) { int i = (high + low) / 2; const FmgrBuiltin *ptr = &fmgr_builtins[i]; if (id == ptr->foid) return ptr; else if (id > ptr->foid) low = i + 1; else high = i - 1; } return NULL;}/* * Lookup a builtin by name. Note there can be more than one entry in * the array with the same name, but they should all point to the same * routine. */static const FmgrBuiltin *fmgr_lookupByName(const char *name){ int i; for (i = 0; i < fmgr_nbuiltins; i++) { if (strcmp(name, fmgr_builtins[i].funcName) == 0) return fmgr_builtins + i; } return NULL;}/* * This routine fills a FmgrInfo struct, given the OID * of the function to be called. * * The caller's CurrentMemoryContext is used as the fn_mcxt of the info * struct; this means that any subsidiary data attached to the info struct * (either by fmgr_info itself, or later on by a function call handler) * will be allocated in that context. The caller must ensure that this * context is at least as long-lived as the info struct itself. This is * not a problem in typical cases where the info struct is on the stack or * in freshly-palloc'd space. However, if one intends to store an info * struct in a long-lived table, it's better to use fmgr_info_cxt. */voidfmgr_info(Oid functionId, FmgrInfo *finfo){ fmgr_info_cxt(functionId, finfo, CurrentMemoryContext);}/* * Fill a FmgrInfo struct, specifying a memory context in which its * subsidiary data should go. */voidfmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt){ fmgr_info_cxt_security(functionId, finfo, mcxt, false);}/* * This one does the actual work. ignore_security is ordinarily false * but is set to true by fmgr_security_definer to avoid infinite * recursive lookups. */static voidfmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, bool ignore_security){ const FmgrBuiltin *fbp; HeapTuple procedureTuple; Form_pg_proc procedureStruct; Datum prosrcdatum; bool isnull; char *prosrc; /* * fn_oid *must* be filled in last. Some code assumes that if fn_oid is * valid, the whole struct is valid. Some FmgrInfo struct's do survive * elogs. */ finfo->fn_oid = InvalidOid; finfo->fn_extra = NULL; finfo->fn_mcxt = mcxt; finfo->fn_expr = NULL; /* caller may set this later */ if ((fbp = fmgr_isbuiltin(functionId)) != NULL) { /* * Fast path for builtin functions: don't bother consulting pg_proc */ finfo->fn_nargs = fbp->nargs; finfo->fn_strict = fbp->strict; finfo->fn_retset = fbp->retset; finfo->fn_addr = fbp->func; finfo->fn_oid = functionId; return; } /* Otherwise we need the pg_proc entry */ procedureTuple = SearchSysCache(PROCOID, ObjectIdGetDatum(functionId), 0, 0, 0); if (!HeapTupleIsValid(procedureTuple)) elog(ERROR, "cache lookup failed for function %u", functionId); procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); finfo->fn_nargs = procedureStruct->pronargs; finfo->fn_strict = procedureStruct->proisstrict; finfo->fn_retset = procedureStruct->proretset; /* * If it has prosecdef set, or non-null proconfig, use * fmgr_security_definer call handler. */ if (!ignore_security && (procedureStruct->prosecdef || !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) { finfo->fn_addr = fmgr_security_definer; finfo->fn_oid = functionId; ReleaseSysCache(procedureTuple); return; } switch (procedureStruct->prolang) { case INTERNALlanguageId: /* * For an ordinary builtin function, we should never get here * because the isbuiltin() search above will have succeeded. * However, if the user has done a CREATE FUNCTION to create an * alias for a builtin function, we can end up here. In that case * we have to look up the function by name. The name of the * internal function is stored in prosrc (it doesn't have to be * the same as the name of the alias!) */ prosrcdatum = SysCacheGetAttr(PROCOID, procedureTuple, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); prosrc = DatumGetCString(DirectFunctionCall1(textout, prosrcdatum)); fbp = fmgr_lookupByName(prosrc); if (fbp == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("internal function \"%s\" is not in internal lookup table", prosrc))); pfree(prosrc); /* Should we check that nargs, strict, retset match the table? */ finfo->fn_addr = fbp->func; break; case ClanguageId: fmgr_info_C_lang(functionId, finfo, procedureTuple); break; case SQLlanguageId: finfo->fn_addr = fmgr_sql; break; default: fmgr_info_other_lang(functionId, finfo, procedureTuple); break; } finfo->fn_oid = functionId; ReleaseSysCache(procedureTuple);}/* * Special fmgr_info processing for C-language functions. Note that * finfo->fn_oid is not valid yet. */static voidfmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple){ Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); CFuncHashTabEntry *hashentry; PGFunction user_fn; const Pg_finfo_record *inforec; Oldstyle_fnextra *fnextra; bool isnull; 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) */ 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) { case 0: /* Old style: need to use a handler */ finfo->fn_addr = fmgr_oldstyle; fnextra = (Oldstyle_fnextra *) MemoryContextAllocZero(finfo->fn_mcxt, sizeof(Oldstyle_fnextra)); finfo->fn_extra = (void *) fnextra; fnextra->func = (func_ptr) user_fn; for (i = 0; i < procedureStruct->pronargs; i++) { fnextra->arg_toastable[i] = TypeIsToastable(procedureStruct->proargtypes.values[i]); } break; case 1: /* New style: call directly */ finfo->fn_addr = user_fn; break; default: /* Shouldn't get here if fetch_finfo_record did its job */ elog(ERROR, "unrecognized function API version: %d", inforec->api_version); break; }}/* * Special fmgr_info processing for other-language functions. Note * that finfo->fn_oid is not valid yet. */static voidfmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple){ Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); Oid language = procedureStruct->prolang; HeapTuple languageTuple; Form_pg_language languageStruct; FmgrInfo plfinfo; languageTuple = SearchSysCache(LANGOID, ObjectIdGetDatum(language), 0, 0, 0); if (!HeapTupleIsValid(languageTuple)) elog(ERROR, "cache lookup failed for language %u", language); languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); fmgr_info(languageStruct->lanplcallfoid, &plfinfo); finfo->fn_addr = plfinfo.fn_addr; /* * If lookup of the PL handler function produced nonnull fn_extra, * complain --- it must be an oldstyle function! We no longer support * oldstyle PL handlers. */ if (plfinfo.fn_extra != NULL) elog(ERROR, "language %u has old-style handler", language); ReleaseSysCache(languageTuple);}/* * Fetch and validate the information record for the given external function. * The function is specified by a handle for the containing library * (obtained from load_external_function) as well as the function name. * * If no info function exists for the given name, it is not an error. * Instead we return a default info record for a version-0 function. * We want to raise an error here only if the info function returns * something bogus. * * This function is broken out of fmgr_info_C_lang so that fmgr_c_validator * can validate the information record for a function not yet entered into * pg_proc. */const Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname){ char *infofuncname; PGFInfoFunction infofunc; const Pg_finfo_record *inforec; static Pg_finfo_record default_inforec = {0}; /* Compute name of info func */ infofuncname = (char *) palloc(strlen(funcname) + 10); strcpy(infofuncname, "pg_finfo_"); strcat(infofuncname, funcname); /* Try to look up the info function */ infofunc = (PGFInfoFunction) lookup_external_function(filehandle, infofuncname); if (infofunc == NULL) { /* Not found --- assume version 0 */ pfree(infofuncname); return &default_inforec; } /* Found, so call it */ inforec = (*infofunc) (); /* Validate result as best we can */ if (inforec == NULL) elog(ERROR, "null result from info function \"%s\"", infofuncname); switch (inforec->api_version) { case 0: case 1: /* OK, no additional fields to validate */ break; default: ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized API version %d reported by info function \"%s\"", inforec->api_version, infofuncname))); break; } pfree(infofuncname); return inforec;}/*------------------------------------------------------------------------- * 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) && ItemPointerEquals(&entry->fn_tid, &procedureTuple->t_self)) 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 voidrecord_C_func(HeapTuple procedureTuple, PGFunction user_fn, const 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 = oid_hash; CFuncHash = hash_create("CFuncHash", 100, &hash_ctl, HASH_ELEM | HASH_FUNCTION); } entry = (CFuncHashTabEntry *) hash_search(CFuncHash, &fn_oid, HASH_ENTER, &found); /* OID is already filled in */ entry->fn_xmin = HeapTupleHeaderGetXmin(procedureTuple->t_data); entry->fn_tid = procedureTuple->t_self; 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. */voidclear_external_function_hash(void *filehandle){ if (CFuncHash) hash_destroy(CFuncHash); CFuncHash = NULL;}/* * Copy an FmgrInfo struct *
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?