postgresql/src/backend/utils/fmgr/fmgr.c

2042 lines
55 KiB
C

/*-------------------------------------------------------------------------
*
* fmgr.c
* The Postgres function manager.
*
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/utils/fmgr/fmgr.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.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 "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"
/*
* Hooks for function calls
*/
PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook = NULL;
PGDLLIMPORT fmgr_hook_type fmgr_hook = NULL;
/*
* Hashtable for fast lookup of external C functions
*/
typedef struct
{
/* fn_oid is the hash key and so must be first! */
Oid fn_oid; /* OID of an external C function */
TransactionId fn_xmin; /* for checking up-to-dateness */
ItemPointerData fn_tid;
PGFunction user_fn; /* the function's address */
const Pg_finfo_record *inforec; /* address of its info record */
} CFuncHashTabEntry;
static HTAB *CFuncHash = NULL;
static void fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
bool ignore_security);
static void fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);
static void fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple);
static CFuncHashTabEntry *lookup_C_func(HeapTuple procedureTuple);
static void record_C_func(HeapTuple procedureTuple,
PGFunction user_fn, const Pg_finfo_record *inforec);
/* 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
* or name, but search by Oid is much faster.
*/
static const FmgrBuiltin *
fmgr_isbuiltin(Oid id)
{
uint16 index;
/* fast lookup only possible if original oid still assigned */
if (id > fmgr_last_builtin_oid)
return NULL;
/*
* 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.
*/
index = fmgr_builtin_oid_index[id];
if (index == InvalidOidBuiltinMapping)
return NULL;
return &fmgr_builtins[index];
}
/*
* Lookup a builtin by name. Note there can be more than one entry in
* the array with the same name, but they should all point to the same
* routine.
*/
static const FmgrBuiltin *
fmgr_lookupByName(const char *name)
{
int i;
for (i = 0; i < fmgr_nbuiltins; i++)
{
if (strcmp(name, fmgr_builtins[i].funcName) == 0)
return fmgr_builtins + i;
}
return NULL;
}
/*
* This routine fills a FmgrInfo struct, given the OID
* of the function to be called.
*
* The caller's CurrentMemoryContext is used as the fn_mcxt of the info
* struct; this means that any subsidiary data attached to the info struct
* (either by fmgr_info itself, or later on by a function call handler)
* will be allocated in that context. The caller must ensure that this
* context is at least as long-lived as the info struct itself. This is
* not a problem in typical cases where the info struct is on the stack or
* in freshly-palloc'd space. However, if one intends to store an info
* struct in a long-lived table, it's better to use fmgr_info_cxt.
*/
void
fmgr_info(Oid functionId, FmgrInfo *finfo)
{
fmgr_info_cxt_security(functionId, finfo, CurrentMemoryContext, false);
}
/*
* Fill a FmgrInfo struct, specifying a memory context in which its
* subsidiary data should go.
*/
void
fmgr_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 when we need to avoid recursion.
*/
static void
fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
bool ignore_security)
{
const FmgrBuiltin *fbp;
HeapTuple procedureTuple;
Form_pg_proc procedureStruct;
Datum prosrcdatum;
bool isnull;
char *prosrc;
/*
* fn_oid *must* be filled in last. Some code assumes that if fn_oid is
* valid, the whole struct is valid. Some FmgrInfo struct's do survive
* elogs.
*/
finfo->fn_oid = InvalidOid;
finfo->fn_extra = NULL;
finfo->fn_mcxt = mcxt;
finfo->fn_expr = NULL; /* caller may set this later */
if ((fbp = fmgr_isbuiltin(functionId)) != NULL)
{
/*
* Fast path for builtin functions: don't bother consulting pg_proc
*/
finfo->fn_nargs = fbp->nargs;
finfo->fn_strict = fbp->strict;
finfo->fn_retset = fbp->retset;
finfo->fn_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 = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionId));
if (!HeapTupleIsValid(procedureTuple))
elog(ERROR, "cache lookup failed for function %u", functionId);
procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
finfo->fn_nargs = procedureStruct->pronargs;
finfo->fn_strict = procedureStruct->proisstrict;
finfo->fn_retset = procedureStruct->proretset;
/*
* If it has prosecdef set, 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, 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;
}
switch (procedureStruct->prolang)
{
case INTERNALlanguageId:
/*
* For an ordinary builtin function, we should never get here
* 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
* internal function is stored in prosrc (it doesn't have to be
* the same as the name of the alias!)
*/
prosrcdatum = SysCacheGetAttr(PROCOID, procedureTuple,
Anum_pg_proc_prosrc, &isnull);
if (isnull)
elog(ERROR, "null prosrc");
prosrc = TextDatumGetCString(prosrcdatum);
fbp = fmgr_lookupByName(prosrc);
if (fbp == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("internal function \"%s\" is not in internal lookup table",
prosrc)));
pfree(prosrc);
/* Should we check that nargs, strict, retset match the table? */
finfo->fn_addr = fbp->func;
/* 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;
}
finfo->fn_oid = functionId;
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.
*/
static void
fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
{
CFuncHashTabEntry *hashentry;
PGFunction user_fn;
const Pg_finfo_record *inforec;
bool isnull;
/*
* See if we have the function address cached already
*/
hashentry = lookup_C_func(procedureTuple);
if (hashentry)
{
user_fn = hashentry->user_fn;
inforec = hashentry->inforec;
}
else
{
Datum prosrcattr,
probinattr;
char *prosrcstring,
*probinstring;
void *libraryhandle;
/*
* Get prosrc and probin strings (link symbol and library filename).
* 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 C function %u", functionId);
prosrcstring = TextDatumGetCString(prosrcattr);
probinattr = SysCacheGetAttr(PROCOID, procedureTuple,
Anum_pg_proc_probin, &isnull);
if (isnull)
elog(ERROR, "null probin for C function %u", functionId);
probinstring = TextDatumGetCString(probinattr);
/* Look up the function itself */
user_fn = load_external_function(probinstring, prosrcstring, true,
&libraryhandle);
/* Get the function information record (real or default) */
inforec = fetch_finfo_record(libraryhandle, prosrcstring);
/* Cache the addresses for later calls */
record_C_func(procedureTuple, user_fn, inforec);
pfree(prosrcstring);
pfree(probinstring);
}
switch (inforec->api_version)
{
case 1:
/* New style: call directly */
finfo->fn_addr = user_fn;
break;
default:
/* Shouldn't get here if fetch_finfo_record did its job */
elog(ERROR, "unrecognized function API version: %d",
inforec->api_version);
break;
}
}
/*
* Special fmgr_info processing for other-language functions. Note
* that finfo->fn_oid is not valid yet.
*/
static void
fmgr_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 = SearchSysCache1(LANGOID, ObjectIdGetDatum(language));
if (!HeapTupleIsValid(languageTuple))
elog(ERROR, "cache lookup failed for language %u", language);
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
/*
* 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.
*/
fmgr_info_cxt_security(languageStruct->lanplcallfoid, &plfinfo,
CurrentMemoryContext, true);
finfo->fn_addr = plfinfo.fn_addr;
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 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, const char *funcname)
{
char *infofuncname;
PGFInfoFunction infofunc;
const Pg_finfo_record *inforec;
infofuncname = psprintf("pg_finfo_%s", funcname);
/* Try to look up the info function */
infofunc = (PGFInfoFunction) lookup_external_function(filehandle,
infofuncname);
if (infofunc == NULL)
{
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 */
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 1:
/* OK, no additional fields to validate */
break;
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unrecognized API version %d reported by info function \"%s\"",
inforec->api_version, infofuncname)));
break;
}
pfree(infofuncname);
return inforec;
}
/*-------------------------------------------------------------------------
* Routines for caching lookup information for external C functions.
*
* The routines in dfmgr.c are relatively slow, so we try to avoid running
* them more than once per external function per session. We use a hash table
* with the function OID as the lookup key.
*-------------------------------------------------------------------------
*/
/*
* lookup_C_func: try to find a C function in the hash table
*
* If an entry exists and is up to date, return it; else return NULL
*/
static CFuncHashTabEntry *
lookup_C_func(HeapTuple procedureTuple)
{
Oid fn_oid = ((Form_pg_proc) GETSTRUCT(procedureTuple))->oid;
CFuncHashTabEntry *entry;
if (CFuncHash == NULL)
return NULL; /* no table yet */
entry = (CFuncHashTabEntry *)
hash_search(CFuncHash,
&fn_oid,
HASH_FIND,
NULL);
if (entry == NULL)
return NULL; /* no such entry */
if (entry->fn_xmin == HeapTupleHeaderGetRawXmin(procedureTuple->t_data) &&
ItemPointerEquals(&entry->fn_tid, &procedureTuple->t_self))
return entry; /* OK */
return NULL; /* entry is out of date */
}
/*
* record_C_func: enter (or update) info about a C function in the hash table
*/
static void
record_C_func(HeapTuple procedureTuple,
PGFunction user_fn, const Pg_finfo_record *inforec)
{
Oid fn_oid = ((Form_pg_proc) GETSTRUCT(procedureTuple))->oid;
CFuncHashTabEntry *entry;
bool found;
/* Create the hash table if it doesn't exist yet */
if (CFuncHash == NULL)
{
HASHCTL hash_ctl;
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Oid);
hash_ctl.entrysize = sizeof(CFuncHashTabEntry);
CFuncHash = hash_create("CFuncHash",
100,
&hash_ctl,
HASH_ELEM | HASH_BLOBS);
}
entry = (CFuncHashTabEntry *)
hash_search(CFuncHash,
&fn_oid,
HASH_ENTER,
&found);
/* OID is already filled in */
entry->fn_xmin = HeapTupleHeaderGetRawXmin(procedureTuple->t_data);
entry->fn_tid = procedureTuple->t_self;
entry->user_fn = user_fn;
entry->inforec = inforec;
}
/*
* clear_external_function_hash: remove entries for a library being closed
*
* Presently we just zap the entire hash table, but later it might be worth
* the effort to remove only the entries associated with the given handle.
*/
void
clear_external_function_hash(void *filehandle)
{
if (CFuncHash)
hash_destroy(CFuncHash);
CFuncHash = NULL;
}
/*
* 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.
*/
void
fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo,
MemoryContext destcxt)
{
memcpy(dstinfo, srcinfo, sizeof(FmgrInfo));
dstinfo->fn_mcxt = destcxt;
dstinfo->fn_extra = NULL;
}
/*
* Specialized lookup routine for fmgr_internal_validator: given the alleged
* name of an internal function, return the OID of the function.
* If the name is not recognized, return InvalidOid.
*/
Oid
fmgr_internal_function(const char *proname)
{
const FmgrBuiltin *fbp = fmgr_lookupByName(proname);
if (fbp == NULL)
return InvalidOid;
return fbp->foid;
}
/*
* 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.
*/
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/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.
*/
extern Datum
fmgr_security_definer(PG_FUNCTION_ARGS)
{
Datum result;
struct fmgr_security_definer_cache *volatile fcache;
FmgrInfo *save_flinfo;
Oid save_userid;
int save_sec_context;
volatile int save_nestlevel;
PgStat_FunctionCallUsage fcusage;
if (!fcinfo->flinfo->fn_extra)
{
HeapTuple tuple;
Form_pg_proc procedureStruct;
Datum datum;
bool isnull;
MemoryContext oldcxt;
fcache = MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
sizeof(*fcache));
fmgr_info_cxt_security(fcinfo->flinfo->fn_oid, &fcache->flinfo,
fcinfo->flinfo->fn_mcxt, true);
fcache->flinfo.fn_expr = fcinfo->flinfo->fn_expr;
tuple = SearchSysCache1(PROCOID,
ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u",
fcinfo->flinfo->fn_oid);
procedureStruct = (Form_pg_proc) GETSTRUCT(tuple);
if (procedureStruct->prosecdef)
fcache->userid = procedureStruct->proowner;
datum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proconfig,
&isnull);
if (!isnull)
{
oldcxt = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
fcache->proconfig = DatumGetArrayTypePCopy(datum);
MemoryContextSwitchTo(oldcxt);
}
ReleaseSysCache(tuple);
fcinfo->flinfo->fn_extra = fcache;
}
else
fcache = fcinfo->flinfo->fn_extra;
/* 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))
SetUserIdAndSecContext(fcache->userid,
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
if (fcache->proconfig)
{
ProcessGUCArray(fcache->proconfig,
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
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
* needed to clean up the flinfo link.
*/
save_flinfo = fcinfo->flinfo;
PG_TRY();
{
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();
fcinfo->flinfo = save_flinfo;
if (fcache->proconfig)
AtEOXact_GUC(true, save_nestlevel);
if (OidIsValid(fcache->userid))
SetUserIdAndSecContext(save_userid, save_sec_context);
if (fmgr_hook)
(*fmgr_hook) (FHET_END, &fcache->flinfo, &fcache->arg);
return result;
}
/*-------------------------------------------------------------------------
* Support routines for callers of fmgr-compatible functions
*-------------------------------------------------------------------------
*/
/*
* 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
* look at FmgrInfo, since there won't be any.
*/
Datum
DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
{
LOCAL_FCINFO(fcinfo, 1);
Datum result;
InitFunctionCallInfoData(*fcinfo, NULL, 1, collation, NULL, NULL);
fcinfo->args[0].value = arg1;
fcinfo->args[0].isnull = false;
result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
{
LOCAL_FCINFO(fcinfo, 2);
Datum result;
InitFunctionCallInfoData(*fcinfo, NULL, 2, collation, NULL, NULL);
fcinfo->args[0].value = arg1;
fcinfo->args[0].isnull = false;
fcinfo->args[1].value = arg2;
fcinfo->args[1].isnull = false;
result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
DirectFunctionCall3Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3)
{
LOCAL_FCINFO(fcinfo, 3);
Datum result;
InitFunctionCallInfoData(*fcinfo, NULL, 3, 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;
result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
DirectFunctionCall4Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4)
{
LOCAL_FCINFO(fcinfo, 4);
Datum result;
InitFunctionCallInfoData(*fcinfo, NULL, 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 = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
DirectFunctionCall5Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5)
{
LOCAL_FCINFO(fcinfo, 5);
Datum result;
InitFunctionCallInfoData(*fcinfo, NULL, 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 = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
DirectFunctionCall6Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6)
{
LOCAL_FCINFO(fcinfo, 6);
Datum result;
InitFunctionCallInfoData(*fcinfo, NULL, 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 = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
DirectFunctionCall7Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7)
{
LOCAL_FCINFO(fcinfo, 7);
Datum result;
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)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
DirectFunctionCall8Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7, Datum arg8)
{
LOCAL_FCINFO(fcinfo, 8);
Datum result;
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)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
DirectFunctionCall9Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7, Datum arg8,
Datum arg9)
{
LOCAL_FCINFO(fcinfo, 9);
Datum result;
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)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
/*
* 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
CallerFInfoFunctionCall1(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1)
{
LOCAL_FCINFO(fcinfo, 1);
Datum result;
InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
fcinfo->args[0].value = arg1;
fcinfo->args[0].isnull = false;
result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
if (fcinfo->isnull)
elog(ERROR, "function %p returned NULL", (void *) func);
return result;
}
Datum
CallerFInfoFunctionCall2(PGFunction func, FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
{
LOCAL_FCINFO(fcinfo, 2);
Datum result;
InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
fcinfo->args[0].value = arg1;
fcinfo->args[0].isnull = false;
fcinfo->args[1].value = arg2;
fcinfo->args[1].isnull = false;
result = (*func) (fcinfo);
/* Check for null result, since caller is clearly not expecting one */
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
FunctionCall0Coll(FmgrInfo *flinfo, Oid collation)
{
LOCAL_FCINFO(fcinfo, 0);
Datum result;
InitFunctionCallInfoData(*fcinfo, flinfo, 0, collation, NULL, NULL);
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;
}
Datum
FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
{
LOCAL_FCINFO(fcinfo, 1);
Datum result;
InitFunctionCallInfoData(*fcinfo, flinfo, 1, collation, NULL, NULL);
fcinfo->args[0].value = arg1;
fcinfo->args[0].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);
return result;
}
Datum
FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
{
LOCAL_FCINFO(fcinfo, 2);
Datum result;
InitFunctionCallInfoData(*fcinfo, flinfo, 2, collation, NULL, NULL);
fcinfo->args[0].value = arg1;
fcinfo->args[0].isnull = false;
fcinfo->args[1].value = arg2;
fcinfo->args[1].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);
return result;
}
Datum
FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3)
{
LOCAL_FCINFO(fcinfo, 3);
Datum result;
InitFunctionCallInfoData(*fcinfo, flinfo, 3, 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;
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;
}
Datum
FunctionCall4Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4)
{
LOCAL_FCINFO(fcinfo, 4);
Datum result;
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", flinfo->fn_oid);
return result;
}
Datum
FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5)
{
LOCAL_FCINFO(fcinfo, 5);
Datum result;
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", flinfo->fn_oid);
return result;
}
Datum
FunctionCall6Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6)
{
LOCAL_FCINFO(fcinfo, 6);
Datum result;
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", flinfo->fn_oid);
return result;
}
Datum
FunctionCall7Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7)
{
LOCAL_FCINFO(fcinfo, 7);
Datum result;
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);
return result;
}
Datum
FunctionCall8Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7, Datum arg8)
{
LOCAL_FCINFO(fcinfo, 8);
Datum result;
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);
return result;
}
Datum
FunctionCall9Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7, Datum arg8,
Datum arg9)
{
LOCAL_FCINFO(fcinfo, 9);
Datum result;
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);
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
OidFunctionCall0Coll(Oid functionId, Oid collation)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall0Coll(&flinfo, collation);
}
Datum
OidFunctionCall1Coll(Oid functionId, Oid collation, Datum arg1)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall1Coll(&flinfo, collation, arg1);
}
Datum
OidFunctionCall2Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall2Coll(&flinfo, collation, arg1, arg2);
}
Datum
OidFunctionCall3Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall3Coll(&flinfo, collation, arg1, arg2, arg3);
}
Datum
OidFunctionCall4Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall4Coll(&flinfo, collation, arg1, arg2, arg3, arg4);
}
Datum
OidFunctionCall5Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall5Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5);
}
Datum
OidFunctionCall6Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall6Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
arg6);
}
Datum
OidFunctionCall7Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall7Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
arg6, arg7);
}
Datum
OidFunctionCall8Coll(Oid functionId, Oid collation, Datum arg1, Datum arg2,
Datum arg3, Datum arg4, Datum arg5,
Datum arg6, Datum arg7, Datum arg8)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return FunctionCall8Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
arg6, arg7, arg8);
}
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;
fmgr_info(functionId, &flinfo);
return FunctionCall9Coll(&flinfo, collation, arg1, arg2, arg3, arg4, arg5,
arg6, arg7, arg8, arg9);
}
/*
* Special cases for convenient invocation of datatype I/O functions.
*/
/*
* Call a previously-looked-up datatype input function.
*
* "str" may be NULL to indicate we are reading a NULL. In this case
* the caller should assume the result is NULL, but we'll call the input
* function anyway if it's not strict. So this is almost but not quite
* the same as FunctionCall3.
*/
Datum
InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
{
LOCAL_FCINFO(fcinfo, 3);
Datum result;
if (str == NULL && flinfo->fn_strict)
return (Datum) 0; /* just return null result */
InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
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);
/* Should get null result if and only if str is NULL */
if (str == NULL)
{
if (!fcinfo->isnull)
elog(ERROR, "input function %u returned non-NULL",
flinfo->fn_oid);
}
else
{
if (fcinfo->isnull)
elog(ERROR, "input function %u returned NULL",
flinfo->fn_oid);
}
return result;
}
/*
* Call a previously-looked-up datatype output function.
*
* Do not call this on NULL datums.
*
* This is currently little more than window dressing for FunctionCall1.
*/
char *
OutputFunctionCall(FmgrInfo *flinfo, Datum val)
{
return DatumGetCString(FunctionCall1(flinfo, val));
}
/*
* Call a previously-looked-up datatype binary-input function.
*
* "buf" may be NULL to indicate we are reading a NULL. In this case
* the caller should assume the result is NULL, but we'll call the receive
* function anyway if it's not strict. So this is almost but not quite
* the same as FunctionCall3.
*/
Datum
ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
Oid typioparam, int32 typmod)
{
LOCAL_FCINFO(fcinfo, 3);
Datum result;
if (buf == NULL && flinfo->fn_strict)
return (Datum) 0; /* just return null result */
InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, NULL, NULL);
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);
/* Should get null result if and only if buf is NULL */
if (buf == NULL)
{
if (!fcinfo->isnull)
elog(ERROR, "receive function %u returned non-NULL",
flinfo->fn_oid);
}
else
{
if (fcinfo->isnull)
elog(ERROR, "receive function %u returned NULL",
flinfo->fn_oid);
}
return result;
}
/*
* Call a previously-looked-up datatype binary-output function.
*
* Do not call this on NULL datums.
*
* 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)
{
return DatumGetByteaP(FunctionCall1(flinfo, val));
}
/*
* As above, for I/O functions identified by OID. These are only to be used
* in seldom-executed code paths. They are not only slow but leak memory.
*/
Datum
OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return InputFunctionCall(&flinfo, str, typioparam, typmod);
}
char *
OidOutputFunctionCall(Oid functionId, Datum val)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return OutputFunctionCall(&flinfo, val);
}
Datum
OidReceiveFunctionCall(Oid functionId, StringInfo buf,
Oid typioparam, int32 typmod)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return ReceiveFunctionCall(&flinfo, buf, typioparam, typmod);
}
bytea *
OidSendFunctionCall(Oid functionId, Datum val)
{
FmgrInfo flinfo;
fmgr_info(functionId, &flinfo);
return SendFunctionCall(&flinfo, val);
}
/*-------------------------------------------------------------------------
* Support routines for standard maybe-pass-by-reference datatypes
*
* int8 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)
{
int64 *retval = (int64 *) palloc(sizeof(int64));
*retval = X;
return PointerGetDatum(retval);
}
Datum
Float8GetDatum(float8 X)
{
float8 *retval = (float8 *) palloc(sizeof(float8));
*retval = X;
return PointerGetDatum(retval);
}
#endif /* USE_FLOAT8_BYVAL */
/*-------------------------------------------------------------------------
* Support routines for toastable datatypes
*-------------------------------------------------------------------------
*/
struct varlena *
pg_detoast_datum(struct varlena *datum)
{
if (VARATT_IS_EXTENDED(datum))
return detoast_attr(datum);
else
return datum;
}
struct varlena *
pg_detoast_datum_copy(struct varlena *datum)
{
if (VARATT_IS_EXTENDED(datum))
return detoast_attr(datum);
else
{
/* Make a modifiable copy of the varlena object */
Size len = VARSIZE(datum);
struct varlena *result = (struct varlena *) palloc(len);
memcpy(result, datum, len);
return result;
}
}
struct varlena *
pg_detoast_datum_slice(struct varlena *datum, int32 first, int32 count)
{
/* Only get the specified portion from the toast rel */
return detoast_attr_slice(datum, first, count);
}
struct varlena *
pg_detoast_datum_packed(struct varlena *datum)
{
if (VARATT_IS_COMPRESSED(datum) || VARATT_IS_EXTERNAL(datum))
return detoast_attr(datum);
else
return datum;
}
/*-------------------------------------------------------------------------
* Support routines for extracting info from fn_expr parse tree
*
* 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.
*-------------------------------------------------------------------------
*/
/*
* Get the actual type OID of the function return type
*
* Returns InvalidOid if information is not available
*/
Oid
get_fn_expr_rettype(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 InvalidOid;
expr = flinfo->fn_expr;
return exprType(expr);
}
/*
* Get the actual type OID of a specific function argument (counting from 0)
*
* Returns InvalidOid if information is not available
*/
Oid
get_fn_expr_argtype(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 InvalidOid;
return get_call_expr_argtype(flinfo->fn_expr, argnum);
}
/*
* Get the actual type OID of a specific function argument (counting from 0),
* but working from the calling expression tree instead of FmgrInfo
*
* Returns InvalidOid if information is not available
*/
Oid
get_call_expr_argtype(Node *expr, int argnum)
{
List *args;
Oid argtype;
if (expr == NULL)
return InvalidOid;
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 InvalidOid;
if (argnum < 0 || argnum >= list_length(args))
return InvalidOid;
argtype = exprType((Node *) list_nth(args, argnum));
/*
* 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_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;
}