1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-30 06:01:21 +03:00
Files
postgres/src/backend/utils/fmgr/fmgr.c
1999-05-10 04:02:07 +00:00

402 lines
10 KiB
C

/*-------------------------------------------------------------------------
*
* fmgr.c
* Interface routines for the table-driven function manager.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.27 1999/05/10 04:02:05 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "postgres.h"
/* these 2 files are generated by Gen_fmgrtab.sh; contain the declarations */
#include "fmgr.h"
#include "utils/fmgrtab.h"
#include "nodes/pg_list.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_language.h"
#include "utils/syscache.h"
#include "nodes/params.h"
#include "utils/builtins.h"
#include "utils/elog.h"
#include "nodes/parsenodes.h"
#include "commands/trigger.h"
/*
* Interface for PL functions
*
* XXX: use of global fmgr_pl_finfo variable is really ugly. FIXME
*/
static char *
fmgr_pl(char *arg0,...)
{
va_list pvar;
FmgrValues values;
int n_arguments = fmgr_pl_finfo->fn_nargs;
bool isNull = false;
int i;
memset(&values, 0, sizeof(values));
if (n_arguments > 0)
{
values.data[0] = arg0;
if (n_arguments > 1)
{
if (n_arguments > MAXFMGRARGS)
elog(ERROR, "fmgr_pl: function %u: too many arguments (%d > %d)",
fmgr_pl_finfo->fn_oid, n_arguments, MAXFMGRARGS);
va_start(pvar, arg0);
for (i = 1; i < n_arguments; i++)
values.data[i] = va_arg(pvar, char *);
va_end(pvar);
}
}
/* Call the PL handler */
CurrentTriggerData = NULL;
return (*(fmgr_pl_finfo->fn_plhandler)) (fmgr_pl_finfo,
&values,
&isNull);
}
/*
* Interface for untrusted functions
*/
static char *
fmgr_untrusted(char *arg0,...)
{
/* Currently these are unsupported. Someday we might do something like
* forking a subprocess to execute 'em.
*/
elog(ERROR, "Untrusted functions not supported.");
return NULL; /* keep compiler happy */
}
/*
* Interface for SQL-language functions
*/
static char *
fmgr_sql(char *arg0,...)
{
/*
* XXX It'd be really nice to support SQL functions anywhere that builtins
* are supported. What would we have to do? What pitfalls are there?
*/
elog(ERROR, "SQL-language function not supported in this context.");
return NULL; /* keep compiler happy */
}
/*
* fmgr_c is not really for C functions only; it can be called for functions
* in any language. Many parts of the system use this entry point if they
* want to pass the arguments in an array rather than as explicit arguments.
*/
char *
fmgr_c(FmgrInfo *finfo,
FmgrValues *values,
bool *isNull)
{
char *returnValue = (char *) NULL;
int n_arguments = finfo->fn_nargs;
func_ptr user_fn = fmgr_faddr(finfo);
/*
* If finfo contains a PL handler for this function, call that
* instead.
*/
if (finfo->fn_plhandler != NULL)
return (*(finfo->fn_plhandler)) (finfo, values, isNull);
if (user_fn == (func_ptr) NULL)
elog(ERROR, "Internal error: fmgr_c received NULL function pointer.");
switch (n_arguments)
{
case 0:
returnValue = (*user_fn) ();
break;
case 1:
/* NullValue() uses isNull to check if args[0] is NULL */
returnValue = (*user_fn) (values->data[0], isNull);
break;
case 2:
returnValue = (*user_fn) (values->data[0], values->data[1]);
break;
case 3:
returnValue = (*user_fn) (values->data[0], values->data[1],
values->data[2]);
break;
case 4:
returnValue = (*user_fn) (values->data[0], values->data[1],
values->data[2], values->data[3]);
break;
case 5:
returnValue = (*user_fn) (values->data[0], values->data[1],
values->data[2], values->data[3],
values->data[4]);
break;
case 6:
returnValue = (*user_fn) (values->data[0], values->data[1],
values->data[2], values->data[3],
values->data[4], values->data[5]);
break;
case 7:
returnValue = (*user_fn) (values->data[0], values->data[1],
values->data[2], values->data[3],
values->data[4], values->data[5],
values->data[6]);
break;
case 8:
returnValue = (*user_fn) (values->data[0], values->data[1],
values->data[2], values->data[3],
values->data[4], values->data[5],
values->data[6], values->data[7]);
break;
case 9:
/*
* XXX Note that functions with >8 arguments can only be
* called from inside the system, not from the user level,
* since the catalogs only store 8 argument types for user
* type-checking!
*/
returnValue = (*user_fn) (values->data[0], values->data[1],
values->data[2], values->data[3],
values->data[4], values->data[5],
values->data[6], values->data[7],
values->data[8]);
break;
default:
elog(ERROR, "fmgr_c: function %u: too many arguments (%d > %d)",
finfo->fn_oid, n_arguments, MAXFMGRARGS);
break;
}
return returnValue;
}
/*
* Expand a regproc OID into an FmgrInfo cache struct.
*/
void
fmgr_info(Oid procedureId, FmgrInfo *finfo)
{
FmgrCall *fcp;
HeapTuple procedureTuple;
FormData_pg_proc *procedureStruct;
HeapTuple languageTuple;
Form_pg_language languageStruct;
Oid language;
char *prosrc;
finfo->fn_addr = NULL;
finfo->fn_plhandler = NULL;
finfo->fn_oid = procedureId;
if ((fcp = fmgr_isbuiltin(procedureId)) != NULL)
{
/* Fast path for builtin functions: don't bother consulting pg_proc */
finfo->fn_addr = fcp->func;
finfo->fn_nargs = fcp->nargs;
}
else
{
procedureTuple = SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(procedureId),
0, 0, 0);
if (!HeapTupleIsValid(procedureTuple))
{
elog(ERROR, "fmgr_info: function %u: cache lookup failed",
procedureId);
}
procedureStruct = (FormData_pg_proc *) GETSTRUCT(procedureTuple);
if (!procedureStruct->proistrusted)
{
finfo->fn_addr = (func_ptr) fmgr_untrusted;
finfo->fn_nargs = procedureStruct->pronargs;
return;
}
language = procedureStruct->prolang;
switch (language)
{
case INTERNALlanguageId:
/*
* For an ordinary builtin function, we should never get here
* because the isbuiltin() search above will have succeeded.
* However, if the user has done a CREATE FUNCTION to create
* an alias for a builtin function, we end up here. In that
* case we have to look up the function by name. The name
* of the internal function is stored in prosrc (it doesn't
* have to be the same as the name of the alias!)
*/
prosrc = textout(&(procedureStruct->prosrc));
finfo->fn_addr = fmgr_lookupByName(prosrc);
if (!finfo->fn_addr)
elog(ERROR, "fmgr_info: function %s not in internal table",
prosrc);
finfo->fn_nargs = procedureStruct->pronargs;
pfree(prosrc);
break;
case ClanguageId:
finfo->fn_addr = fmgr_dynamic(procedureId, &(finfo->fn_nargs));
break;
case SQLlanguageId:
finfo->fn_addr = (func_ptr) fmgr_sql;
finfo->fn_nargs = procedureStruct->pronargs;
break;
default:
/*
* Might be a created procedural language Lookup the
* syscache for the language and check the lanispl flag If
* this is the case, we return a NULL function pointer and
* the number of arguments from the procedure.
*/
languageTuple = SearchSysCacheTuple(LANOID,
ObjectIdGetDatum(procedureStruct->prolang),
0, 0, 0);
if (!HeapTupleIsValid(languageTuple))
{
elog(ERROR, "fmgr_info: %s %u",
"Cache lookup for language failed",
DatumGetObjectId(procedureStruct->prolang));
}
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
if (languageStruct->lanispl)
{
FmgrInfo plfinfo;
fmgr_info(languageStruct->lanplcallfoid, &plfinfo);
finfo->fn_addr = (func_ptr) fmgr_pl;
finfo->fn_plhandler = plfinfo.fn_addr;
finfo->fn_nargs = procedureStruct->pronargs;
}
else
{
elog(ERROR, "fmgr_info: function %u: unknown language %d",
procedureId, language);
}
break;
}
}
}
/*
* fmgr - return the value of a function call
*
* If the function is a system routine, it's compiled in, so call
* it directly.
*
* Otherwise pass it to the the appropriate 'language' function caller.
*
* Returns the return value of the invoked function if succesful,
* 0 if unsuccessful.
*/
char *
fmgr(Oid procedureId,...)
{
va_list pvar;
int i;
int pronargs;
FmgrValues values;
FmgrInfo finfo;
bool isNull = false;
fmgr_info(procedureId, &finfo);
pronargs = finfo.fn_nargs;
if (pronargs > MAXFMGRARGS)
elog(ERROR, "fmgr: function %u: too many arguments (%d > %d)",
procedureId, pronargs, MAXFMGRARGS);
va_start(pvar, procedureId);
for (i = 0; i < pronargs; ++i)
values.data[i] = va_arg(pvar, char *);
va_end(pvar);
/* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */
return fmgr_c(&finfo, &values, &isNull);
}
/*
* This is just a version of fmgr() in which the hacker can prepend a C
* function pointer. This routine is not normally called; generally,
* if you have all of this information you're likely to just jump through
* the pointer, but it's available for use with macros in fmgr.h if you
* want this routine to do sanity-checking for you.
*
* funcinfo, n_arguments, args...
*/
#ifdef TRACE_FMGR_PTR
char *
fmgr_ptr(FmgrInfo *finfo,...)
{
va_list pvar;
int i;
int n_arguments;
FmgrInfo local_finfo;
FmgrValues values;
bool isNull = false;
local_finfo->fn_addr = finfo->fn_addr;
local_finfo->fn_plhandler = finfo->fn_plhandler;
local_finfo->fn_oid = finfo->fn_oid;
va_start(pvar, finfo);
n_arguments = va_arg(pvar, int);
local_finfo->fn_nargs = n_arguments;
if (n_arguments > MAXFMGRARGS)
{
elog(ERROR, "fmgr_ptr: function %u: too many arguments (%d > %d)",
func_id, n_arguments, MAXFMGRARGS);
}
for (i = 0; i < n_arguments; ++i)
values.data[i] = va_arg(pvar, char *);
va_end(pvar);
/* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */
return fmgr_c(&local_finfo, &values, &isNull);
}
#endif
/*
* This routine is not well thought out. When I get around to adding a
* function pointer field to FuncIndexInfo, it will be replace by calls
* to fmgr_c().
*/
char *
fmgr_array_args(Oid procedureId, int nargs, char *args[], bool *isNull)
{
FmgrInfo finfo;
fmgr_info(procedureId, &finfo);
finfo.fn_nargs = nargs;
/* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */
return fmgr_c(&finfo,
(FmgrValues *) args,
isNull);
}