X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Ffmgr%2Ffmgr.c;h=099ebd779ba26d2d2ee5f019d7d9f9e1505c0d82;hb=HEAD;hp=44667e62be4137ea41c74b960192a0072a152f23;hpb=2f0f7b4bce13e68394543728801ef011fd82fac6;p=postgresql diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 44667e62be..099ebd779b 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -3,62 +3,38 @@ * fmgr.c * The Postgres function manager. * - * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2019, 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.115 2008/04/17 21:37:28 alvherre Exp $ + * src/backend/utils/fmgr/fmgr.c * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/heapam.h" -#include "access/tuptoaster.h" +#include "access/detoast.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "executor/functions.h" +#include "lib/stringinfo.h" #include "miscadmin.h" -#include "parser/parse_expr.h" +#include "nodes/nodeFuncs.h" +#include "pgstat.h" +#include "utils/acl.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(). + * Hooks for function calls */ -#if (defined(__mc68000__) || (defined(__m68k__))) && defined(__ELF__) -typedef int32 (*func_ptr) (); -#else -typedef 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; +PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook = NULL; +PGDLLIMPORT fmgr_hook_type fmgr_hook = NULL; /* * Hashtable for fast lookup of external C functions @@ -70,51 +46,47 @@ typedef struct 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 */ + 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); + 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); + PGFunction user_fn, const Pg_finfo_record *inforec); + +/* extern so it's callable via JIT */ +extern Datum fmgr_security_definer(PG_FUNCTION_ARGS); /* - * Lookup routines for builtin-function table. We can search by either Oid + * 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; + uint16 index; + + /* fast lookup only possible if original oid still assigned */ + if (id > fmgr_last_builtin_oid) + return NULL; /* - * Loop invariant: low is the first index that could contain target entry, - * and high is the last index that could contain it. + * Lookup function data. If there's a miss in that range it's likely a + * nonexistent function, returning NULL here will trigger an ERROR later. */ - 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; + index = fmgr_builtin_oid_index[id]; + if (index == InvalidOidBuiltinMapping) + return NULL; + + return &fmgr_builtins[index]; } /* @@ -151,7 +123,7 @@ fmgr_lookupByName(const char *name) void fmgr_info(Oid functionId, FmgrInfo *finfo) { - fmgr_info_cxt(functionId, finfo, CurrentMemoryContext); + fmgr_info_cxt_security(functionId, finfo, CurrentMemoryContext, false); } /* @@ -166,8 +138,7 @@ fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt) /* * 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. + * but is set to true when we need to avoid recursion. */ static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, @@ -198,15 +169,14 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, finfo->fn_nargs = fbp->nargs; finfo->fn_strict = fbp->strict; finfo->fn_retset = fbp->retset; + finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ 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); + procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); if (!HeapTupleIsValid(procedureTuple)) elog(ERROR, "cache lookup failed for function %u", functionId); procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); @@ -216,14 +186,26 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, finfo->fn_retset = procedureStruct->proretset; /* - * If it has prosecdef set, or non-null proconfig, use - * fmgr_security_definer call handler. + * If it has prosecdef set, non-null proconfig, or if a plugin wants to + * hook function entry/exit, use fmgr_security_definer call handler --- + * unless we are being called again by fmgr_security_definer or + * fmgr_info_other_lang. + * + * When using fmgr_security_definer, function stats tracking is always + * disabled at the outer level, and instead we set the flag properly in + * fmgr_security_definer's private flinfo and implement the tracking + * inside fmgr_security_definer. This loses the ability to charge the + * overhead of fmgr_security_definer to the function, but gains the + * ability to set the track_functions GUC as a local GUC parameter of an + * interesting function and have the right things happen. */ if (!ignore_security && (procedureStruct->prosecdef || - !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) + !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig, NULL) || + FmgrHookIsNeeded(functionId))) { finfo->fn_addr = fmgr_security_definer; + finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ finfo->fn_oid = functionId; ReleaseSysCache(procedureTuple); return; @@ -235,7 +217,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, /* * For an ordinary builtin function, we should never get here - * because the isbuiltin() search above will have succeeded. + * because the fmgr_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 @@ -256,18 +238,23 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, pfree(prosrc); /* Should we check that nargs, strict, retset match the table? */ finfo->fn_addr = fbp->func; + /* note this policy is also assumed in fast path above */ + finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ break; case ClanguageId: fmgr_info_C_lang(functionId, finfo, procedureTuple); + finfo->fn_stats = TRACK_FUNC_PL; /* ie, track if ALL */ break; case SQLlanguageId: finfo->fn_addr = fmgr_sql; + finfo->fn_stats = TRACK_FUNC_PL; /* ie, track if ALL */ break; default: fmgr_info_other_lang(functionId, finfo, procedureTuple); + finfo->fn_stats = TRACK_FUNC_OFF; /* ie, track if not OFF */ break; } @@ -275,6 +262,95 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, ReleaseSysCache(procedureTuple); } +/* + * Return module and C function name providing implementation of functionId. + * + * If *mod == NULL and *fn == NULL, no C symbol is known to implement + * function. + * + * If *mod == NULL and *fn != NULL, the function is implemented by a symbol in + * the main binary. + * + * If *mod != NULL and *fn !=NULL the function is implemented in an extension + * shared object. + * + * The returned module and function names are pstrdup'ed into the current + * memory context. + */ +void +fmgr_symbol(Oid functionId, char **mod, char **fn) +{ + HeapTuple procedureTuple; + Form_pg_proc procedureStruct; + bool isnull; + Datum prosrcattr; + Datum probinattr; + + /* Otherwise we need the pg_proc entry */ + procedureTuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId)); + if (!HeapTupleIsValid(procedureTuple)) + elog(ERROR, "cache lookup failed for function %u", functionId); + procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + + /* + */ + if (procedureStruct->prosecdef || + !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig, NULL) || + FmgrHookIsNeeded(functionId)) + { + *mod = NULL; /* core binary */ + *fn = pstrdup("fmgr_security_definer"); + ReleaseSysCache(procedureTuple); + return; + } + + /* see fmgr_info_cxt_security for the individual cases */ + switch (procedureStruct->prolang) + { + case INTERNALlanguageId: + prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple, + Anum_pg_proc_prosrc, &isnull); + if (isnull) + elog(ERROR, "null prosrc"); + + *mod = NULL; /* core binary */ + *fn = TextDatumGetCString(prosrcattr); + break; + + case ClanguageId: + prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple, + Anum_pg_proc_prosrc, &isnull); + if (isnull) + elog(ERROR, "null prosrc for C function %u", functionId); + + probinattr = SysCacheGetAttr(PROCOID, procedureTuple, + Anum_pg_proc_probin, &isnull); + if (isnull) + elog(ERROR, "null probin for C function %u", functionId); + + /* + * No need to check symbol presence / API version here, already + * checked in fmgr_info_cxt_security. + */ + *mod = TextDatumGetCString(probinattr); + *fn = TextDatumGetCString(prosrcattr); + break; + + case SQLlanguageId: + *mod = NULL; /* core binary */ + *fn = pstrdup("fmgr_sql"); + break; + + default: + *mod = NULL; + *fn = NULL; /* unknown, pass pointer */ + break; + } + + ReleaseSysCache(procedureTuple); +} + + /* * Special fmgr_info processing for C-language functions. Note that * finfo->fn_oid is not valid yet. @@ -282,13 +358,10 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, static void fmgr_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 @@ -308,18 +381,20 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) void *libraryhandle; /* - * Get prosrc and probin strings (link symbol and library filename) + * Get prosrc and probin strings (link symbol and library filename). + * While in general these columns might be null, that's not allowed + * for C-language functions. */ prosrcattr = SysCacheGetAttr(PROCOID, procedureTuple, Anum_pg_proc_prosrc, &isnull); if (isnull) - elog(ERROR, "null prosrc for function %u", functionId); + elog(ERROR, "null prosrc for C function %u", functionId); prosrcstring = TextDatumGetCString(prosrcattr); probinattr = SysCacheGetAttr(PROCOID, procedureTuple, Anum_pg_proc_probin, &isnull); if (isnull) - elog(ERROR, "null probin for function %u", functionId); + elog(ERROR, "null probin for C function %u", functionId); probinstring = TextDatumGetCString(probinattr); /* Look up the function itself */ @@ -338,20 +413,6 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) 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; @@ -377,23 +438,19 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) Form_pg_language languageStruct; FmgrInfo plfinfo; - languageTuple = SearchSysCache(LANGOID, - ObjectIdGetDatum(language), - 0, 0, 0); + languageTuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(language)); 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. + * Look up the language's call handler function, ignoring any attributes + * that would normally cause insertion of fmgr_security_definer. We need + * to get back a bare pointer to the actual C-language function. */ - if (plfinfo.fn_extra != NULL) - elog(ERROR, "language %u has old-style handler", language); + fmgr_info_cxt_security(languageStruct->lanplcallfoid, &plfinfo, + CurrentMemoryContext, true); + finfo->fn_addr = plfinfo.fn_addr; ReleaseSysCache(languageTuple); } @@ -403,36 +460,32 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) * 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. + * If no info function exists for the given name an error is raised. * * 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) +fetch_finfo_record(void *filehandle, const 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); + infofuncname = psprintf("pg_finfo_%s", 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; + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not find function information for function \"%s\"", + funcname), + errhint("SQL-callable functions need an accompanying PG_FUNCTION_INFO_V1(funcname)."))); + return NULL; /* silence compiler */ } /* Found, so call it */ @@ -443,7 +496,6 @@ fetch_finfo_record(void *filehandle, char *funcname) elog(ERROR, "null result from info function \"%s\"", infofuncname); switch (inforec->api_version) { - case 0: case 1: /* OK, no additional fields to validate */ break; @@ -477,7 +529,7 @@ fetch_finfo_record(void *filehandle, char *funcname) static CFuncHashTabEntry * lookup_C_func(HeapTuple procedureTuple) { - Oid fn_oid = HeapTupleGetOid(procedureTuple); + Oid fn_oid = ((Form_pg_proc) GETSTRUCT(procedureTuple))->oid; CFuncHashTabEntry *entry; if (CFuncHash == NULL) @@ -489,7 +541,7 @@ lookup_C_func(HeapTuple procedureTuple) NULL); if (entry == NULL) return NULL; /* no such entry */ - if (entry->fn_xmin == HeapTupleHeaderGetXmin(procedureTuple->t_data) && + if (entry->fn_xmin == HeapTupleHeaderGetRawXmin(procedureTuple->t_data) && ItemPointerEquals(&entry->fn_tid, &procedureTuple->t_self)) return entry; /* OK */ return NULL; /* entry is out of date */ @@ -502,7 +554,7 @@ static void record_C_func(HeapTuple procedureTuple, PGFunction user_fn, const Pg_finfo_record *inforec) { - Oid fn_oid = HeapTupleGetOid(procedureTuple); + Oid fn_oid = ((Form_pg_proc) GETSTRUCT(procedureTuple))->oid; CFuncHashTabEntry *entry; bool found; @@ -514,11 +566,10 @@ record_C_func(HeapTuple procedureTuple, 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); + HASH_ELEM | HASH_BLOBS); } entry = (CFuncHashTabEntry *) @@ -527,7 +578,7 @@ record_C_func(HeapTuple procedureTuple, HASH_ENTER, &found); /* OID is already filled in */ - entry->fn_xmin = HeapTupleHeaderGetXmin(procedureTuple->t_data); + entry->fn_xmin = HeapTupleHeaderGetRawXmin(procedureTuple->t_data); entry->fn_tid = procedureTuple->t_self; entry->user_fn = user_fn; entry->inforec = inforec; @@ -552,7 +603,7 @@ clear_external_function_hash(void *filehandle) * 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, + * language-dependent subsidiary info. We cheat by zeroing fn_extra, * instead, meaning that subsidiary info will have to be recomputed. */ void @@ -561,18 +612,7 @@ fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, { 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; + dstinfo->fn_extra = NULL; } @@ -593,246 +633,7 @@ fmgr_internal_function(const char *proname) /* - * Handler for old-style "C" language functions - */ -static Datum -fmgr_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 = (char *) (*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 = (char *) (*user_fn) (fcinfo->arg[0], - &fcinfo->isnull); - break; - case 2: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1]); - break; - case 3: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2]); - break; - case 4: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3]); - break; - case 5: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4]); - break; - case 6: - returnValue = (char *) (*user_fn) (fcinfo->arg[0], - fcinfo->arg[1], - fcinfo->arg[2], - fcinfo->arg[3], - fcinfo->arg[4], - fcinfo->arg[5]); - break; - case 7: - returnValue = (char *) (*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 = (char *) (*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 = (char *) (*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 = (char *) (*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 = (char *) (*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], - fcinfo->arg[10]); - break; - case 12: - returnValue = (char *) (*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], - fcinfo->arg[10], - fcinfo->arg[11]); - break; - case 13: - returnValue = (char *) (*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], - fcinfo->arg[10], - fcinfo->arg[11], - fcinfo->arg[12]); - break; - case 14: - returnValue = (char *) (*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], - fcinfo->arg[10], - fcinfo->arg[11], - fcinfo->arg[12], - fcinfo->arg[13]); - break; - case 15: - returnValue = (char *) (*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], - fcinfo->arg[10], - fcinfo->arg[11], - fcinfo->arg[12], - fcinfo->arg[13], - fcinfo->arg[14]); - break; - case 16: - returnValue = (char *) (*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], - fcinfo->arg[10], - fcinfo->arg[11], - fcinfo->arg[12], - fcinfo->arg[13], - fcinfo->arg[14], - fcinfo->arg[15]); - break; - default: - - /* - * Increasing FUNC_MAX_ARGS doesn't automatically add cases to the - * above code, so mention the actual value in this error not - * FUNC_MAX_ARGS. You could add cases to the above if you needed - * to support old-style functions with many arguments, but making - * 'em be new-style is probably a better idea. - */ - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("function %u has too many arguments (%d, maximum is %d)", - fcinfo->flinfo->fn_oid, n_arguments, 16))); - returnValue = NULL; /* keep compiler quiet */ - break; - } - - return PointerGetDatum(returnValue); -} - - -/* - * Support for security-definer and proconfig-using functions. We support + * Support for security-definer and proconfig-using functions. We support * both of these features using the same call handler, because they are * often used together and it would be inefficient (as well as notationally * messy) to have two levels of call handler involved. @@ -842,27 +643,29 @@ struct fmgr_security_definer_cache FmgrInfo flinfo; /* lookup info for target function */ Oid userid; /* userid to set, or InvalidOid */ ArrayType *proconfig; /* GUC values to set, or NULL */ + Datum arg; /* passthrough argument for plugin modules */ }; /* - * Function handler for security-definer/proconfig functions. We extract the - * OID of the actual function and do a fmgr lookup again. Then we fetch the - * pg_proc row and copy the owner ID and proconfig fields. (All this info - * is cached for the duration of the current query.) To execute a call, - * we temporarily replace the flinfo with the cached/looked-up one, while - * keeping the outer fcinfo (which contains all the actual arguments, etc.) - * intact. This is not re-entrant, but then the fcinfo itself can't be used - * re-entrantly anyway. + * Function handler for security-definer/proconfig/plugin-hooked functions. + * We extract the OID of the actual function and do a fmgr lookup again. + * Then we fetch the pg_proc row and copy the owner ID and proconfig fields. + * (All this info is cached for the duration of the current query.) + * To execute a call, we temporarily replace the flinfo with the cached + * and looked-up one, while keeping the outer fcinfo (which contains all + * the actual arguments, etc.) intact. This is not re-entrant, but then + * the fcinfo itself can't be used reentrantly anyway. */ -static Datum +extern Datum fmgr_security_definer(PG_FUNCTION_ARGS) { Datum result; struct fmgr_security_definer_cache *volatile fcache; FmgrInfo *save_flinfo; Oid save_userid; - bool save_secdefcxt; + int save_sec_context; volatile int save_nestlevel; + PgStat_FunctionCallUsage fcusage; if (!fcinfo->flinfo->fn_extra) { @@ -879,9 +682,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) fcinfo->flinfo->fn_mcxt, true); fcache->flinfo.fn_expr = fcinfo->flinfo->fn_expr; - tuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(fcinfo->flinfo->fn_oid), - 0, 0, 0); + tuple = SearchSysCache1(PROCOID, + ObjectIdGetDatum(fcinfo->flinfo->fn_oid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for function %u", fcinfo->flinfo->fn_oid); @@ -906,15 +708,16 @@ fmgr_security_definer(PG_FUNCTION_ARGS) else fcache = fcinfo->flinfo->fn_extra; - /* GetUserIdAndContext is cheap enough that no harm in a wasted call */ - GetUserIdAndContext(&save_userid, &save_secdefcxt); + /* GetUserIdAndSecContext is cheap enough that no harm in a wasted call */ + GetUserIdAndSecContext(&save_userid, &save_sec_context); if (fcache->proconfig) /* Need a new GUC nesting level */ save_nestlevel = NewGUCNestLevel(); else save_nestlevel = 0; /* keep compiler quiet */ if (OidIsValid(fcache->userid)) - SetUserIdAndContext(fcache->userid, true); + SetUserIdAndSecContext(fcache->userid, + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); if (fcache->proconfig) { @@ -924,6 +727,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS) GUC_ACTION_SAVE); } + /* function manager hook */ + if (fmgr_hook) + (*fmgr_hook) (FHET_START, &fcache->flinfo, &fcache->arg); + /* * We don't need to restore GUC or userid settings on error, because the * ensuing xact or subxact abort will do that. The PG_TRY block is only @@ -935,11 +742,25 @@ fmgr_security_definer(PG_FUNCTION_ARGS) { fcinfo->flinfo = &fcache->flinfo; + /* See notes in fmgr_info_cxt_security */ + pgstat_init_function_usage(fcinfo, &fcusage); + result = FunctionCallInvoke(fcinfo); + + /* + * We could be calling either a regular or a set-returning function, + * so we have to test to see what finalize flag to use. + */ + pgstat_end_function_usage(&fcusage, + (fcinfo->resultinfo == NULL || + !IsA(fcinfo->resultinfo, ReturnSetInfo) || + ((ReturnSetInfo *) fcinfo->resultinfo)->isDone != ExprMultipleResult)); } PG_CATCH(); { fcinfo->flinfo = save_flinfo; + if (fmgr_hook) + (*fmgr_hook) (FHET_ABORT, &fcache->flinfo, &fcache->arg); PG_RE_THROW(); } PG_END_TRY(); @@ -949,7 +770,9 @@ fmgr_security_definer(PG_FUNCTION_ARGS) if (fcache->proconfig) AtEOXact_GUC(true, save_nestlevel); if (OidIsValid(fcache->userid)) - SetUserIdAndContext(save_userid, save_secdefcxt); + SetUserIdAndSecContext(save_userid, save_sec_context); + if (fmgr_hook) + (*fmgr_hook) (FHET_END, &fcache->flinfo, &fcache->arg); return result; } @@ -963,844 +786,732 @@ fmgr_security_definer(PG_FUNCTION_ARGS) /* * These are for invocation of a specifically named function with a * directly-computed parameter list. Note that neither arguments nor result - * are allowed to be NULL. Also, the function cannot be one that needs to + * are allowed to be NULL. Also, the function cannot be one that needs to * look at FmgrInfo, since there won't be any. */ Datum -DirectFunctionCall1(PGFunction func, Datum arg1) +DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 1); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 1, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, NULL, 1, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.argnull[0] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; - result = (*func) (&fcinfo); + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -DirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2) +DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 2); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 2, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, NULL, 2, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; - result = (*func) (&fcinfo); + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -DirectFunctionCall3(PGFunction func, Datum arg1, Datum arg2, - Datum arg3) +DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, + Datum arg3) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 3); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 3, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, NULL, 3, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; - result = (*func) (&fcinfo); + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -DirectFunctionCall4(PGFunction func, Datum arg1, Datum arg2, - Datum arg3, Datum arg4) +DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 4); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 4, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, NULL, 4, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; - result = (*func) (&fcinfo); + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -DirectFunctionCall5(PGFunction func, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5) +DirectFunctionCall5Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 5); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 5, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, NULL, 5, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; - result = (*func) (&fcinfo); + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -DirectFunctionCall6(PGFunction func, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6) +DirectFunctionCall6Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 6); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 6, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, NULL, 6, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + fcinfo->args[5].value = arg6; + fcinfo->args[5].isnull = false; - result = (*func) (&fcinfo); + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -DirectFunctionCall7(PGFunction func, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7) +DirectFunctionCall7Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 7); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 7, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - - result = (*func) (&fcinfo); + InitFunctionCallInfoData(*fcinfo, NULL, 7, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + fcinfo->args[5].value = arg6; + fcinfo->args[5].isnull = false; + fcinfo->args[6].value = arg7; + fcinfo->args[6].isnull = false; + + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -DirectFunctionCall8(PGFunction func, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7, Datum arg8) +DirectFunctionCall8Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7, Datum arg8) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 8); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 8, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.arg[7] = arg8; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - fcinfo.argnull[7] = false; - - result = (*func) (&fcinfo); + InitFunctionCallInfoData(*fcinfo, NULL, 8, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + fcinfo->args[5].value = arg6; + fcinfo->args[5].isnull = false; + fcinfo->args[6].value = arg7; + fcinfo->args[6].isnull = false; + fcinfo->args[7].value = arg8; + fcinfo->args[7].isnull = false; + + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7, Datum arg8, - Datum arg9) +DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7, Datum arg8, + Datum arg9) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 9); Datum result; - InitFunctionCallInfoData(fcinfo, NULL, 9, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.arg[7] = arg8; - fcinfo.arg[8] = arg9; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - fcinfo.argnull[7] = false; - fcinfo.argnull[8] = false; - - result = (*func) (&fcinfo); + InitFunctionCallInfoData(*fcinfo, NULL, 9, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + fcinfo->args[5].value = arg6; + fcinfo->args[5].isnull = false; + fcinfo->args[6].value = arg7; + fcinfo->args[6].isnull = false; + fcinfo->args[7].value = arg8; + fcinfo->args[7].isnull = false; + fcinfo->args[8].value = arg9; + fcinfo->args[8].isnull = false; + + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "function %p returned NULL", (void *) func); return result; } - /* - * These are for invocation of a previously-looked-up function with a - * directly-computed parameter list. Note that neither arguments nor result - * are allowed to be NULL. + * These functions work like the DirectFunctionCall functions except that + * they use the flinfo parameter to initialise the fcinfo for the call. + * It's recommended that the callee only use the fn_extra and fn_mcxt + * fields, as other fields will typically describe the calling function + * not the callee. Conversely, the calling function should not have + * used fn_extra, unless its use is known to be compatible with the callee's. */ + Datum -FunctionCall1(FmgrInfo *flinfo, Datum arg1) +CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 1); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 1, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.argnull[0] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; - result = FunctionCallInvoke(&fcinfo); + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %p returned NULL", (void *) func); return result; } Datum -FunctionCall2(FmgrInfo *flinfo, Datum arg1, Datum arg2) +CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2) { - /* - * XXX if you change this routine, see also the inlined version in - * utils/sort/tuplesort.c! - */ - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 2); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 2, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; - result = FunctionCallInvoke(&fcinfo); + result = (*func) (fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %p returned NULL", (void *) func); return result; } +/* + * These are for invocation of a previously-looked-up function with a + * directly-computed parameter list. Note that neither arguments nor result + * are allowed to be NULL. + */ Datum -FunctionCall3(FmgrInfo *flinfo, Datum arg1, Datum arg2, - Datum arg3) +FunctionCall0Coll(FmgrInfo *flinfo, Oid collation) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 0); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 3, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; + InitFunctionCallInfoData(*fcinfo, flinfo, 0, collation, NULL, NULL); - result = FunctionCallInvoke(&fcinfo); + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } Datum -FunctionCall4(FmgrInfo *flinfo, Datum arg1, Datum arg2, - Datum arg3, Datum arg4) +FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 1); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 4, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; - result = FunctionCallInvoke(&fcinfo); + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } Datum -FunctionCall5(FmgrInfo *flinfo, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5) +FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 2); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 5, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; - result = FunctionCallInvoke(&fcinfo); + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } Datum -FunctionCall6(FmgrInfo *flinfo, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6) +FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, + Datum arg3) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 3); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 6, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, flinfo, 3, collation, NULL, NULL); - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; - result = FunctionCallInvoke(&fcinfo); + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } Datum -FunctionCall7(FmgrInfo *flinfo, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7) +FunctionCall4Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 4); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 7, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - - result = FunctionCallInvoke(&fcinfo); + InitFunctionCallInfoData(*fcinfo, flinfo, 4, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } Datum -FunctionCall8(FmgrInfo *flinfo, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7, Datum arg8) +FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 5); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 8, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.arg[7] = arg8; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - fcinfo.argnull[7] = false; - - result = FunctionCallInvoke(&fcinfo); + InitFunctionCallInfoData(*fcinfo, flinfo, 5, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } Datum -FunctionCall9(FmgrInfo *flinfo, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7, Datum arg8, - Datum arg9) +FunctionCall6Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 6); Datum result; - InitFunctionCallInfoData(fcinfo, flinfo, 9, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.arg[7] = arg8; - fcinfo.arg[8] = arg9; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - fcinfo.argnull[7] = false; - fcinfo.argnull[8] = false; - - result = FunctionCallInvoke(&fcinfo); + InitFunctionCallInfoData(*fcinfo, flinfo, 6, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + fcinfo->args[5].value = arg6; + fcinfo->args[5].isnull = false; + + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", fcinfo.flinfo->fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } - -/* - * These are for invocation of a function identified by OID with a - * directly-computed parameter list. Note that neither arguments nor result - * are allowed to be NULL. These are essentially fmgr_info() followed - * by FunctionCallN(). If the same function is to be invoked repeatedly, - * do the fmgr_info() once and then use FunctionCallN(). - */ Datum -OidFunctionCall1(Oid functionId, Datum arg1) +FunctionCall7Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7) { - FmgrInfo flinfo; - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 7); Datum result; - fmgr_info(functionId, &flinfo); - - InitFunctionCallInfoData(fcinfo, &flinfo, 1, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.argnull[0] = false; - - result = FunctionCallInvoke(&fcinfo); + InitFunctionCallInfoData(*fcinfo, flinfo, 7, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + fcinfo->args[5].value = arg6; + fcinfo->args[5].isnull = false; + fcinfo->args[6].value = arg7; + fcinfo->args[6].isnull = false; + + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } Datum -OidFunctionCall2(Oid functionId, Datum arg1, Datum arg2) +FunctionCall8Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7, Datum arg8) { - FmgrInfo flinfo; - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 8); Datum result; - fmgr_info(functionId, &flinfo); - - InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - - result = FunctionCallInvoke(&fcinfo); + InitFunctionCallInfoData(*fcinfo, flinfo, 8, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + fcinfo->args[5].value = arg6; + fcinfo->args[5].isnull = false; + fcinfo->args[6].value = arg7; + fcinfo->args[6].isnull = false; + fcinfo->args[7].value = arg8; + fcinfo->args[7].isnull = false; + + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } Datum -OidFunctionCall3(Oid functionId, Datum arg1, Datum arg2, - Datum arg3) +FunctionCall9Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7, Datum arg8, + Datum arg9) { - FmgrInfo flinfo; - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 9); Datum result; - fmgr_info(functionId, &flinfo); - - InitFunctionCallInfoData(fcinfo, &flinfo, 3, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - - result = FunctionCallInvoke(&fcinfo); + InitFunctionCallInfoData(*fcinfo, flinfo, 9, collation, NULL, NULL); + + fcinfo->args[0].value = arg1; + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = arg2; + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = arg3; + fcinfo->args[2].isnull = false; + fcinfo->args[3].value = arg4; + fcinfo->args[3].isnull = false; + fcinfo->args[4].value = arg5; + fcinfo->args[4].isnull = false; + fcinfo->args[5].value = arg6; + fcinfo->args[5].isnull = false; + fcinfo->args[6].value = arg7; + fcinfo->args[6].isnull = false; + fcinfo->args[7].value = arg8; + fcinfo->args[7].isnull = false; + fcinfo->args[8].value = arg9; + fcinfo->args[8].isnull = false; + + result = FunctionCallInvoke(fcinfo); /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); + if (fcinfo->isnull) + elog(ERROR, "function %u returned NULL", flinfo->fn_oid); return result; } + +/* + * These are for invocation of a function identified by OID with a + * directly-computed parameter list. Note that neither arguments nor result + * are allowed to be NULL. These are essentially fmgr_info() followed + * by FunctionCallN(). If the same function is to be invoked repeatedly, + * do the fmgr_info() once and then use FunctionCallN(). + */ Datum -OidFunctionCall4(Oid functionId, Datum arg1, Datum arg2, - Datum arg3, Datum arg4) +OidFunctionCall0Coll(Oid functionId, Oid collation) { FmgrInfo flinfo; - FunctionCallInfoData fcinfo; - Datum result; fmgr_info(functionId, &flinfo); - InitFunctionCallInfoData(fcinfo, &flinfo, 4, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; + return FunctionCall0Coll(&flinfo, collation); +} - result = FunctionCallInvoke(&fcinfo); +Datum +OidFunctionCall1Coll(Oid functionId, Oid collation, Datum arg1) +{ + FmgrInfo flinfo; - /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); + fmgr_info(functionId, &flinfo); - return result; + return FunctionCall1Coll(&flinfo, collation, arg1); } Datum -OidFunctionCall5(Oid functionId, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5) +OidFunctionCall2Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2) { FmgrInfo flinfo; - FunctionCallInfoData fcinfo; - Datum result; fmgr_info(functionId, &flinfo); - InitFunctionCallInfoData(fcinfo, &flinfo, 5, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; + return FunctionCall2Coll(&flinfo, collation, arg1, arg2); +} - result = FunctionCallInvoke(&fcinfo); +Datum +OidFunctionCall3Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2, + Datum arg3) +{ + FmgrInfo flinfo; - /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); + fmgr_info(functionId, &flinfo); - return result; + return FunctionCall3Coll(&flinfo, collation, arg1, arg2, arg3); } Datum -OidFunctionCall6(Oid functionId, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6) +OidFunctionCall4Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4) { FmgrInfo flinfo; - FunctionCallInfoData fcinfo; - Datum result; fmgr_info(functionId, &flinfo); - InitFunctionCallInfoData(fcinfo, &flinfo, 6, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; + return FunctionCall4Coll(&flinfo, collation, arg1, arg2, arg3, arg4); +} - result = FunctionCallInvoke(&fcinfo); +Datum +OidFunctionCall5Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5) +{ + FmgrInfo flinfo; - /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); + fmgr_info(functionId, &flinfo); - return result; + return FunctionCall5Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5); } Datum -OidFunctionCall7(Oid functionId, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7) +OidFunctionCall6Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6) { FmgrInfo flinfo; - FunctionCallInfoData fcinfo; - Datum result; fmgr_info(functionId, &flinfo); - InitFunctionCallInfoData(fcinfo, &flinfo, 7, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - - result = FunctionCallInvoke(&fcinfo); - - /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); - - return result; + return FunctionCall6Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5, + arg6); } Datum -OidFunctionCall8(Oid functionId, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7, Datum arg8) +OidFunctionCall7Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7) { FmgrInfo flinfo; - FunctionCallInfoData fcinfo; - Datum result; fmgr_info(functionId, &flinfo); - InitFunctionCallInfoData(fcinfo, &flinfo, 8, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.arg[7] = arg8; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - fcinfo.argnull[7] = false; - - result = FunctionCallInvoke(&fcinfo); - - /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); - - return result; + return FunctionCall7Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5, + arg6, arg7); } Datum -OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2, - Datum arg3, Datum arg4, Datum arg5, - Datum arg6, Datum arg7, Datum arg8, - Datum arg9) +OidFunctionCall8Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7, Datum arg8) { FmgrInfo flinfo; - FunctionCallInfoData fcinfo; - Datum result; fmgr_info(functionId, &flinfo); - InitFunctionCallInfoData(fcinfo, &flinfo, 9, NULL, NULL); - - fcinfo.arg[0] = arg1; - fcinfo.arg[1] = arg2; - fcinfo.arg[2] = arg3; - fcinfo.arg[3] = arg4; - fcinfo.arg[4] = arg5; - fcinfo.arg[5] = arg6; - fcinfo.arg[6] = arg7; - fcinfo.arg[7] = arg8; - fcinfo.arg[8] = arg9; - fcinfo.argnull[0] = false; - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; - fcinfo.argnull[3] = false; - fcinfo.argnull[4] = false; - fcinfo.argnull[5] = false; - fcinfo.argnull[6] = false; - fcinfo.argnull[7] = false; - fcinfo.argnull[8] = false; - - result = FunctionCallInvoke(&fcinfo); + return FunctionCall8Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5, + arg6, arg7, arg8); +} - /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); +Datum +OidFunctionCall9Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2, + Datum arg3, Datum arg4, Datum arg5, + Datum arg6, Datum arg7, Datum arg8, + Datum arg9) +{ + FmgrInfo flinfo; - return result; + fmgr_info(functionId, &flinfo); + + return FunctionCall9Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5, + arg6, arg7, arg8, arg9); } @@ -1819,35 +1530,35 @@ OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2, Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 3); Datum result; if (str == NULL && flinfo->fn_strict) return (Datum) 0; /* just return null result */ - InitFunctionCallInfoData(fcinfo, flinfo, 3, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, NULL, NULL); - fcinfo.arg[0] = CStringGetDatum(str); - fcinfo.arg[1] = ObjectIdGetDatum(typioparam); - fcinfo.arg[2] = Int32GetDatum(typmod); - fcinfo.argnull[0] = (str == NULL); - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; + fcinfo->args[0].value = CStringGetDatum(str); + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = ObjectIdGetDatum(typioparam); + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = Int32GetDatum(typmod); + fcinfo->args[2].isnull = false; - result = FunctionCallInvoke(&fcinfo); + result = FunctionCallInvoke(fcinfo); /* Should get null result if and only if str is NULL */ if (str == NULL) { - if (!fcinfo.isnull) + if (!fcinfo->isnull) elog(ERROR, "input function %u returned non-NULL", - fcinfo.flinfo->fn_oid); + flinfo->fn_oid); } else { - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "input function %u returned NULL", - fcinfo.flinfo->fn_oid); + flinfo->fn_oid); } return result; @@ -1858,8 +1569,7 @@ InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod) * * Do not call this on NULL datums. * - * This is mere window dressing for FunctionCall1, but its use is recommended - * anyway so that code invoking output functions can be identified easily. + * This is currently little more than window dressing for FunctionCall1. */ char * OutputFunctionCall(FmgrInfo *flinfo, Datum val) @@ -1879,35 +1589,35 @@ Datum ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, Oid typioparam, int32 typmod) { - FunctionCallInfoData fcinfo; + LOCAL_FCINFO(fcinfo, 3); Datum result; if (buf == NULL && flinfo->fn_strict) return (Datum) 0; /* just return null result */ - InitFunctionCallInfoData(fcinfo, flinfo, 3, NULL, NULL); + InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, NULL, NULL); - fcinfo.arg[0] = PointerGetDatum(buf); - fcinfo.arg[1] = ObjectIdGetDatum(typioparam); - fcinfo.arg[2] = Int32GetDatum(typmod); - fcinfo.argnull[0] = (buf == NULL); - fcinfo.argnull[1] = false; - fcinfo.argnull[2] = false; + fcinfo->args[0].value = PointerGetDatum(buf); + fcinfo->args[0].isnull = false; + fcinfo->args[1].value = ObjectIdGetDatum(typioparam); + fcinfo->args[1].isnull = false; + fcinfo->args[2].value = Int32GetDatum(typmod); + fcinfo->args[2].isnull = false; - result = FunctionCallInvoke(&fcinfo); + result = FunctionCallInvoke(fcinfo); /* Should get null result if and only if buf is NULL */ if (buf == NULL) { - if (!fcinfo.isnull) + if (!fcinfo->isnull) elog(ERROR, "receive function %u returned non-NULL", - fcinfo.flinfo->fn_oid); + flinfo->fn_oid); } else { - if (fcinfo.isnull) + if (fcinfo->isnull) elog(ERROR, "receive function %u returned NULL", - fcinfo.flinfo->fn_oid); + flinfo->fn_oid); } return result; @@ -1918,9 +1628,9 @@ ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf, * * Do not call this on NULL datums. * - * This is little more than window dressing for FunctionCall1, but its use is - * recommended anyway so that code invoking output functions can be identified - * easily. Note however that it does guarantee a non-toasted result. + * This is little more than window dressing for FunctionCall1, but it does + * guarantee a non-toasted result, which strictly speaking the underlying + * function doesn't. */ bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val) @@ -1970,92 +1680,32 @@ OidSendFunctionCall(Oid functionId, Datum val) } -/* - * !!! OLD INTERFACE !!! - * - * fmgr() is the only remaining vestige of the old-style caller support - * functions. It's no longer used anywhere in the Postgres distribution, - * but we should leave it around for a release or two to ease the transition - * for user-supplied C functions. OidFunctionCallN() replaces it for new - * code. - * - * DEPRECATED, DO NOT USE IN NEW CODE - */ -char * -fmgr(Oid procedureId,...) -{ - FmgrInfo flinfo; - FunctionCallInfoData fcinfo; - int n_arguments; - Datum result; - - fmgr_info(procedureId, &flinfo); - - MemSet(&fcinfo, 0, sizeof(fcinfo)); - fcinfo.flinfo = &flinfo; - fcinfo.nargs = flinfo.fn_nargs; - n_arguments = fcinfo.nargs; - - if (n_arguments > 0) - { - va_list pvar; - int i; - - if (n_arguments > FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("function %u has too many arguments (%d, maximum is %d)", - flinfo.fn_oid, n_arguments, FUNC_MAX_ARGS))); - va_start(pvar, procedureId); - for (i = 0; i < n_arguments; i++) - fcinfo.arg[i] = PointerGetDatum(va_arg(pvar, char *)); - va_end(pvar); - } - - result = FunctionCallInvoke(&fcinfo); - - /* Check for null result, since caller is clearly not expecting one */ - if (fcinfo.isnull) - elog(ERROR, "function %u returned NULL", flinfo.fn_oid); - - return DatumGetPointer(result); -} - - /*------------------------------------------------------------------------- - * Support routines for standard pass-by-reference datatypes + * Support routines for standard maybe-pass-by-reference datatypes * - * Note: at some point, at least on some platforms, these might become - * pass-by-value types. Obviously Datum must be >= 8 bytes to allow - * int64 or float8 to be pass-by-value. I think that Float4GetDatum - * and Float8GetDatum will need to be out-of-line routines anyway, - * since just casting from float to Datum will not do the right thing; - * some kind of trick with pointer-casting or a union will be needed. + * int8, float4, and float8 can be passed by value if Datum is wide enough. + * (For backwards-compatibility reasons, we allow pass-by-ref to be chosen + * at compile time even if pass-by-val is possible.) + * + * Note: there is only one switch controlling the pass-by-value option for + * both int8 and float8; this is to avoid making things unduly complicated + * for the timestamp types, which might have either representation. *------------------------------------------------------------------------- */ +#ifndef USE_FLOAT8_BYVAL /* controls int8 too */ + Datum Int64GetDatum(int64 X) { -#ifndef INT64_IS_BUSTED int64 *retval = (int64 *) palloc(sizeof(int64)); *retval = X; return PointerGetDatum(retval); -#else /* INT64_IS_BUSTED */ - - /* - * On a machine with no 64-bit-int C datatype, sizeof(int64) will not be - * 8, but we want Int64GetDatum to return an 8-byte object anyway, with - * zeroes in the unused bits. This is needed so that, for example, hash - * join of int8 will behave properly. - */ - int64 *retval = (int64 *) palloc0(Max(sizeof(int64), 8)); - - *retval = X; - return PointerGetDatum(retval); -#endif /* INT64_IS_BUSTED */ } +#endif /* USE_FLOAT8_BYVAL */ + +#ifndef USE_FLOAT4_BYVAL Datum Float4GetDatum(float4 X) @@ -2065,6 +1715,9 @@ Float4GetDatum(float4 X) *retval = X; return PointerGetDatum(retval); } +#endif + +#ifndef USE_FLOAT8_BYVAL Datum Float8GetDatum(float8 X) @@ -2074,6 +1727,8 @@ Float8GetDatum(float8 X) *retval = X; return PointerGetDatum(retval); } +#endif + /*------------------------------------------------------------------------- * Support routines for toastable datatypes @@ -2081,19 +1736,19 @@ Float8GetDatum(float8 X) */ struct varlena * -pg_detoast_datum(struct varlena * datum) +pg_detoast_datum(struct varlena *datum) { if (VARATT_IS_EXTENDED(datum)) - return heap_tuple_untoast_attr(datum); + return detoast_attr(datum); else return datum; } struct varlena * -pg_detoast_datum_copy(struct varlena * datum) +pg_detoast_datum_copy(struct varlena *datum) { if (VARATT_IS_EXTENDED(datum)) - return heap_tuple_untoast_attr(datum); + return detoast_attr(datum); else { /* Make a modifiable copy of the varlena object */ @@ -2106,17 +1761,17 @@ pg_detoast_datum_copy(struct varlena * datum) } struct varlena * -pg_detoast_datum_slice(struct varlena * datum, int32 first, int32 count) +pg_detoast_datum_slice(struct varlena *datum, int32 first, int32 count) { /* Only get the specified portion from the toast rel */ - return heap_tuple_untoast_attr_slice(datum, first, count); + return detoast_attr_slice(datum, first, count); } struct varlena * -pg_detoast_datum_packed(struct varlena * datum) +pg_detoast_datum_packed(struct varlena *datum) { if (VARATT_IS_COMPRESSED(datum) || VARATT_IS_EXTERNAL(datum)) - return heap_tuple_untoast_attr(datum); + return detoast_attr(datum); else return datum; } @@ -2126,6 +1781,8 @@ pg_detoast_datum_packed(struct varlena * datum) * * These are needed by polymorphic functions, which accept multiple possible * input types and need help from the parser to know what they've got. + * Also, some functions might be interested in whether a parameter is constant. + * Functions taking VARIADIC ANY also need to know about the VARIADIC keyword. *------------------------------------------------------------------------- */ @@ -2192,10 +1849,10 @@ get_call_expr_argtype(Node *expr, int argnum) args = ((DistinctExpr *) expr)->args; else if (IsA(expr, ScalarArrayOpExpr)) args = ((ScalarArrayOpExpr *) expr)->args; - else if (IsA(expr, ArrayCoerceExpr)) - args = list_make1(((ArrayCoerceExpr *) expr)->arg); else if (IsA(expr, NullIfExpr)) args = ((NullIfExpr *) expr)->args; + else if (IsA(expr, WindowFunc)) + args = ((WindowFunc *) expr)->args; else return InvalidOid; @@ -2205,16 +1862,195 @@ get_call_expr_argtype(Node *expr, int argnum) argtype = exprType((Node *) list_nth(args, argnum)); /* - * special hack for ScalarArrayOpExpr and ArrayCoerceExpr: what the - * underlying function will actually get passed is the element type of the - * array. + * special hack for ScalarArrayOpExpr: what the underlying function will + * actually get passed is the element type of the array. */ if (IsA(expr, ScalarArrayOpExpr) && argnum == 1) - argtype = get_element_type(argtype); - else if (IsA(expr, ArrayCoerceExpr) && - argnum == 0) - argtype = get_element_type(argtype); + argtype = get_base_element_type(argtype); return argtype; } + +/* + * Find out whether a specific function argument is constant for the + * duration of a query + * + * Returns false if information is not available + */ +bool +get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum) +{ + /* + * can't return anything useful if we have no FmgrInfo or if its fn_expr + * node has not been initialized + */ + if (!flinfo || !flinfo->fn_expr) + return false; + + return get_call_expr_arg_stable(flinfo->fn_expr, argnum); +} + +/* + * Find out whether a specific function argument is constant for the + * duration of a query, but working from the calling expression tree + * + * Returns false if information is not available + */ +bool +get_call_expr_arg_stable(Node *expr, int argnum) +{ + List *args; + Node *arg; + + if (expr == NULL) + return false; + + if (IsA(expr, FuncExpr)) + args = ((FuncExpr *) expr)->args; + else if (IsA(expr, OpExpr)) + args = ((OpExpr *) expr)->args; + else if (IsA(expr, DistinctExpr)) + args = ((DistinctExpr *) expr)->args; + else if (IsA(expr, ScalarArrayOpExpr)) + args = ((ScalarArrayOpExpr *) expr)->args; + else if (IsA(expr, NullIfExpr)) + args = ((NullIfExpr *) expr)->args; + else if (IsA(expr, WindowFunc)) + args = ((WindowFunc *) expr)->args; + else + return false; + + if (argnum < 0 || argnum >= list_length(args)) + return false; + + arg = (Node *) list_nth(args, argnum); + + /* + * Either a true Const or an external Param will have a value that doesn't + * change during the execution of the query. In future we might want to + * consider other cases too, e.g. now(). + */ + if (IsA(arg, Const)) + return true; + if (IsA(arg, Param) && + ((Param *) arg)->paramkind == PARAM_EXTERN) + return true; + + return false; +} + +/* + * Get the VARIADIC flag from the function invocation + * + * Returns false (the default assumption) if information is not available + * + * Note this is generally only of interest to VARIADIC ANY functions + */ +bool +get_fn_expr_variadic(FmgrInfo *flinfo) +{ + Node *expr; + + /* + * can't return anything useful if we have no FmgrInfo or if its fn_expr + * node has not been initialized + */ + if (!flinfo || !flinfo->fn_expr) + return false; + + expr = flinfo->fn_expr; + + if (IsA(expr, FuncExpr)) + return ((FuncExpr *) expr)->funcvariadic; + else + return false; +} + +/*------------------------------------------------------------------------- + * Support routines for procedural language implementations + *------------------------------------------------------------------------- + */ + +/* + * Verify that a validator is actually associated with the language of a + * particular function and that the user has access to both the language and + * the function. All validators should call this before doing anything + * substantial. Doing so ensures a user cannot achieve anything with explicit + * calls to validators that he could not achieve with CREATE FUNCTION or by + * simply calling an existing function. + * + * When this function returns false, callers should skip all validation work + * and call PG_RETURN_VOID(). This never happens at present; it is reserved + * for future expansion. + * + * In particular, checking that the validator corresponds to the function's + * language allows untrusted language validators to assume they process only + * superuser-chosen source code. (Untrusted language call handlers, by + * definition, do assume that.) A user lacking the USAGE language privilege + * would be unable to reach the validator through CREATE FUNCTION, so we check + * that to block explicit calls as well. Checking the EXECUTE privilege on + * the function is often superfluous, because most users can clone the + * function to get an executable copy. It is meaningful against users with no + * database TEMP right and no permanent schema CREATE right, thereby unable to + * create any function. Also, if the function tracks persistent state by + * function OID or name, validating the original function might permit more + * mischief than creating and validating a clone thereof. + */ +bool +CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid) +{ + HeapTuple procTup; + HeapTuple langTup; + Form_pg_proc procStruct; + Form_pg_language langStruct; + AclResult aclresult; + + /* + * Get the function's pg_proc entry. Throw a user-facing error for bad + * OID, because validators can be called with user-specified OIDs. + */ + procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid)); + if (!HeapTupleIsValid(procTup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function with OID %u does not exist", functionOid))); + procStruct = (Form_pg_proc) GETSTRUCT(procTup); + + /* + * Fetch pg_language entry to know if this is the correct validation + * function for that pg_proc entry. + */ + langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(procStruct->prolang)); + if (!HeapTupleIsValid(langTup)) + elog(ERROR, "cache lookup failed for language %u", procStruct->prolang); + langStruct = (Form_pg_language) GETSTRUCT(langTup); + + if (langStruct->lanvalidator != validatorOid) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("language validation function %u called for language %u instead of %u", + validatorOid, procStruct->prolang, + langStruct->lanvalidator))); + + /* first validate that we have permissions to use the language */ + aclresult = pg_language_aclcheck(procStruct->prolang, GetUserId(), + ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, OBJECT_LANGUAGE, + NameStr(langStruct->lanname)); + + /* + * Check whether we are allowed to execute the function itself. If we can + * execute it, there should be no possible side-effect of + * compiling/validation that execution can't have. + */ + aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, OBJECT_FUNCTION, NameStr(procStruct->proname)); + + ReleaseSysCache(procTup); + ReleaseSysCache(langTup); + + return true; +}