/*-------------------------------------------------------------------------
 *
 * 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.8 1998/01/05 03:34:44 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdarg.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/elog.h"

#include "nodes/parsenodes.h"
#include "commands/trigger.h"


char	   *
fmgr_pl(Oid func_id,
		int n_arguments,
		FmgrValues * values,
		bool * isNull)
{
	HeapTuple	procedureTuple;
	HeapTuple	languageTuple;
	Form_pg_proc procedureStruct;
	Form_pg_language languageStruct;
	func_ptr	plcall_fn;
	int			plcall_nargs;

	/* Fetch the pg_proc tuple from the syscache */
	procedureTuple = SearchSysCacheTuple(PROOID,
										 ObjectIdGetDatum(func_id),
										 0, 0, 0);
	if (!HeapTupleIsValid(procedureTuple))
	{
		elog(ABORT, "fmgr_pl(): Cache lookup of procedure %ld failed.",
			 ObjectIdGetDatum(func_id));
	}
	procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);

	/* Fetch the pg_language tuple from the syscache */
	languageTuple = SearchSysCacheTuple(LANOID,
							  ObjectIdGetDatum(procedureStruct->prolang),
										0, 0, 0);
	if (!HeapTupleIsValid(languageTuple))
	{
		elog(ABORT, "fmgr_pl(): Cache lookup of language %ld for procedure %ld failed.",
			 ObjectIdGetDatum(procedureStruct->prolang),
			 ObjectIdGetDatum(func_id));
	}
	languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);

	/* Get the function pointer for the PL call handler */
	fmgr_info(languageStruct->lanplcallfoid, &plcall_fn, &plcall_nargs);
	if (plcall_fn == NULL)
	{
		elog(ABORT, "fmgr_pl(): failed to load PL handler for procedure %ld.",
			 ObjectIdGetDatum(func_id));
	}

	/* Call the PL handler */
	CurrentTriggerData = NULL;
	return (*plcall_fn) (func_id,
						 n_arguments,
						 values,
						 isNull);
}


char	   *
fmgr_c(func_ptr user_fn,
	   Oid func_id,
	   int n_arguments,
	   FmgrValues * values,
	   bool * isNull)
{
	char	   *returnValue = (char *) NULL;


	if (user_fn == (func_ptr) NULL)
	{

		/*
		 * a NULL func_ptr denotet untrusted function (in postgres 4.2).
		 * Untrusted functions have very limited use and is clumsy. We now
		 * use this feature for procedural languages.
		 */
		return fmgr_pl(func_id, n_arguments, values, isNull);
	}

	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(ABORT, "fmgr_c: function %d: too many arguments (%d > %d)",
				 func_id, n_arguments, MAXFMGRARGS);
			break;
	}
	return (returnValue);
}

void
fmgr_info(Oid procedureId, func_ptr * function, int *nargs)
{
	func_ptr	user_fn = NULL;
	FmgrCall   *fcp;
	HeapTuple	procedureTuple;
	FormData_pg_proc *procedureStruct;
	HeapTuple	languageTuple;
	Form_pg_language languageStruct;
	Oid			language;

	if (!(fcp = fmgr_isbuiltin(procedureId)))
	{
		procedureTuple = SearchSysCacheTuple(PROOID,
										   ObjectIdGetDatum(procedureId),
											 0, 0, 0);
		if (!HeapTupleIsValid(procedureTuple))
		{
			elog(ABORT, "fmgr_info: function %d: cache lookup failed\n",
				 procedureId);
		}
		procedureStruct = (FormData_pg_proc *)
			GETSTRUCT(procedureTuple);
		if (!procedureStruct->proistrusted)
		{
			*function = (func_ptr) NULL;
			*nargs = procedureStruct->pronargs;
			return;
		}
		language = procedureStruct->prolang;
		switch (language)
		{
			case INTERNALlanguageId:
				user_fn = fmgr_lookupByName(procedureStruct->proname.data);
				if (!user_fn)
					elog(ABORT, "fmgr_info: function %s: not in internal table",
						 procedureStruct->proname.data);
				break;
			case ClanguageId:
				user_fn = fmgr_dynamic(procedureId, nargs);
				break;
			case SQLlanguageId:
				user_fn = (func_ptr) NULL;
				*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(ABORT, "fmgr_info: %s %ld",
						 "Cache lookup for language %d failed",
						 ObjectIdGetDatum(procedureStruct->prolang));
				}
				languageStruct = (Form_pg_language)
					GETSTRUCT(languageTuple);
				if (languageStruct->lanispl)
				{
					user_fn = (func_ptr) NULL;
					*nargs = procedureStruct->pronargs;
				}
				else
				{
					elog(ABORT, "fmgr_info: function %d: unknown language %d",
						 procedureId, language);
				}
				break;
		}
	}
	else
	{
		user_fn = fcp->func;
		*nargs = fcp->nargs;
	}
	*function = user_fn;
}

/*
 *		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;
	register	i;
	int			pronargs;
	FmgrValues	values;
	func_ptr	user_fn;
	bool		isNull = false;

	va_start(pvar, procedureId);

	fmgr_info(procedureId, &user_fn, &pronargs);

	if (pronargs > MAXFMGRARGS)
	{
		elog(ABORT, "fmgr: function %d: too many arguments (%d > %d)",
			 procedureId, pronargs, MAXFMGRARGS);
	}
	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(user_fn, procedureId, pronargs, &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.
 *
 * func_ptr, func_id, n_arguments, args...
 */
#ifdef NOT_USED
char	   *
fmgr_ptr(func_ptr user_fn, Oid func_id,...)
{
	va_list		pvar;
	register	i;
	int			n_arguments;
	FmgrValues	values;
	bool		isNull = false;

	va_start(pvar, func_id);
	n_arguments = va_arg(pvar, int);
	if (n_arguments > MAXFMGRARGS)
	{
		elog(ABORT, "fmgr_ptr: function %d: 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(user_fn, func_id, n_arguments, &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)
{
	func_ptr	user_fn;
	int			true_arguments;

	fmgr_info(procedureId, &user_fn, &true_arguments);

	/* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */
	return
		(fmgr_c(user_fn,
				procedureId,
				true_arguments,
				(FmgrValues *) args,
				isNull));
}
