/*------------------------------------------------------------------------- * * 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.32 1999/07/17 20:18:04 momjian Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "commands/trigger.h" #include "utils/builtins.h" #include "utils/fmgrtab.h" #include "utils/syscache.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); }