fmgr.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,673 行 · 第 1/3 页
C
1,673 行
/*------------------------------------------------------------------------- * * fmgr.c * The Postgres function manager. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.76 2003/09/25 06:58:05 petere Exp $ * *------------------------------------------------------------------------- */#include "postgres.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/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 pink 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... */#if defined(__mc68000__) && 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;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 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 (const FmgrBuiltin *) 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 (const FmgrBuiltin *) 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; 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 (procedureStruct->prosecdef && !ignore_security) { 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!) */ prosrc = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(&procedureStruct->prosrc))); 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); Datum prosrcattr, probinattr; char *prosrcstring, *probinstring; void *libraryhandle; 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)); 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); switch (inforec->api_version) { case 0: /* Old style: need to use a handler */ finfo->fn_addr = fmgr_oldstyle; fnextra = (Oldstyle_fnextra *) MemoryContextAlloc(finfo->fn_mcxt, sizeof(Oldstyle_fnextra)); finfo->fn_extra = (void *) fnextra; MemSet(fnextra, 0, sizeof(Oldstyle_fnextra)); fnextra->func = (func_ptr) user_fn; for (i = 0; i < procedureStruct->pronargs; i++) { fnextra->arg_toastable[i] = TypeIsToastable(procedureStruct->proargtypes[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; } pfree(prosrcstring); pfree(probinstring);}/* * 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 ProcedureCreate() * can validate the information record for a function not yet entered into * pg_proc. */Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname){ char *infofuncname; PGFInfoFunction infofunc; 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 == (PGFInfoFunction) 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;}/* * Copy an FmgrInfo struct * * This is inherently somewhat bogus since we can't reliably duplicate * language-dependent subsidiary info. We cheat by zeroing fn_extra, * instead, meaning that subsidiary info will have to be recomputed. */voidfmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, MemoryContext destcxt){ memcpy(dstinfo, srcinfo, sizeof(FmgrInfo)); dstinfo->fn_mcxt = destcxt; if (dstinfo->fn_addr == fmgr_oldstyle) { /* For oldstyle functions we must copy fn_extra */ Oldstyle_fnextra *fnextra; fnextra = (Oldstyle_fnextra *) MemoryContextAlloc(destcxt, sizeof(Oldstyle_fnextra)); memcpy(fnextra, srcinfo->fn_extra, sizeof(Oldstyle_fnextra)); dstinfo->fn_extra = (void *) fnextra; } else dstinfo->fn_extra = NULL;}/* * Specialized lookup routine for ProcedureCreate(): given the alleged name * of an internal function, return the OID of the function. * If the name is not recognized, return InvalidOid. */Oidfmgr_internal_function(const char *proname){ const FmgrBuiltin *fbp = fmgr_lookupByName(proname); if (fbp == NULL) return InvalidOid; return fbp->foid;}/* * Handler for old-style "C" language functions */static Datumfmgr_oldstyle(PG_FUNCTION_ARGS){ Oldstyle_fnextra *fnextra; int n_arguments = fcinfo->nargs; int i; bool isnull; func_ptr user_fn; char *returnValue; if (fcinfo->flinfo == NULL || fcinfo->flinfo->fn_extra == NULL) elog(ERROR, "fmgr_oldstyle received NULL pointer"); fnextra = (Oldstyle_fnextra *) fcinfo->flinfo->fn_extra; /* * Result is NULL if any argument is NULL, but we still call the * function (peculiar, but that's the way it worked before, and after * all this is a backwards-compatibility wrapper). Note, however, * that we'll never get here with NULL arguments if the function is * marked strict. * * We also need to detoast any TOAST-ed inputs, since it's unlikely that * an old-style function knows about TOASTing. */ isnull = false; for (i = 0; i < n_arguments; i++) { if (PG_ARGISNULL(i)) isnull = true; else if (fnextra->arg_toastable[i]) fcinfo->arg[i] = PointerGetDatum(PG_DETOAST_DATUM(fcinfo->arg[i])); } fcinfo->isnull = isnull; user_fn = fnextra->func; switch (n_arguments) { case 0: returnValue = (*user_fn) (); break; case 1: /* * nullvalue() used to use isNull to check if arg is NULL; * perhaps there are other functions still out there that also * rely on this undocumented hack? */ returnValue = (*user_fn) (fcinfo->arg[0], &fcinfo->isnull); break; case 2: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1]); break; case 3: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1], fcinfo->arg[2]); break; case 4: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1], fcinfo->arg[2], fcinfo->arg[3]); break; case 5: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1], fcinfo->arg[2], fcinfo->arg[3], fcinfo->arg[4]); break; case 6: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1], fcinfo->arg[2], fcinfo->arg[3], fcinfo->arg[4], fcinfo->arg[5]); break; case 7: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1], fcinfo->arg[2], fcinfo->arg[3], fcinfo->arg[4], fcinfo->arg[5], fcinfo->arg[6]); break; case 8: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1], fcinfo->arg[2], fcinfo->arg[3], fcinfo->arg[4], fcinfo->arg[5], fcinfo->arg[6], fcinfo->arg[7]); break; case 9: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1], fcinfo->arg[2], fcinfo->arg[3], fcinfo->arg[4], fcinfo->arg[5], fcinfo->arg[6], fcinfo->arg[7], fcinfo->arg[8]); break; case 10: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1], fcinfo->arg[2], fcinfo->arg[3], fcinfo->arg[4], fcinfo->arg[5], fcinfo->arg[6], fcinfo->arg[7], fcinfo->arg[8], fcinfo->arg[9]); break; case 11: returnValue = (*user_fn) (fcinfo->arg[0], fcinfo->arg[1],
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?