/*----------------------------------------------------------------
 *   FILE
 *	fmgr.c
 *
 *   DESCRIPTION
 *	Interface routines for the table-driven function manager.
 *
 *   NOTES
 *
 *   IDENTIFICATION
 *	/usr/local/devel/postgres-v4r2/src/backend/utils/fmgr/RCS/fmgr.c,v 1.98 1993/01/05 02:31:00 aoki Exp
 *----------------------------------------------------------------
 */

#include <stdio.h>
#include <varargs.h>

#include "tmp/c.h"
#include "tmp/postgres.h"

#include "fmgr.h"
#include "utils/fmgrtab.h"

#include "nodes/pg_lisp.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_language.h"
#include "catalog/syscache.h"
#include "rules/params.h"

#include "utils/log.h"

RcsId("/usr/local/devel/postgres-v4r2/src/backend/utils/fmgr/RCS/fmgr.c,v 1.98 1993/01/05 02:31:00 aoki Exp");

char *
fmgr_c(user_fn, func_id, n_arguments, values, isNull)
	func_ptr	user_fn;
	ObjectId	func_id;
	int 		n_arguments;
	FmgrValues	*values;
	Boolean		*isNull;		
{
	char	*returnValue = (char *) NULL;
	
	if (user_fn == (func_ptr) NULL)		/* untrusted function */
		return((char *) fmgr_ufp(func_id, (Datum *) values));
	
	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(WARN, "fmgr_c: function %d: too many arguments (%d > %d)",
		     func_id, n_arguments, MAXFMGRARGS);
		break;
	}
	return(returnValue);
}

void
fmgr_info(procedureId, function, nargs)
	ObjectId	procedureId;
	func_ptr	*function;
	int		*nargs;
{
	func_ptr		user_fn;
	FmgrCall		*fcp;
	HeapTuple		procedureTuple;
	FormData_pg_proc	*procedureStruct;
	ObjectId		language;

	if (!(fcp = fmgr_isbuiltin(procedureId))) {
		procedureTuple = SearchSysCacheTuple(PROOID,
						     (char *) procedureId,
						     NULL, NULL, NULL);
		if (!HeapTupleIsValid(procedureTuple)) {
			elog(WARN, "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:
			elog(WARN, "fmgr_info: function %d: not in internal table",
			     procedureId);
			break;
		case ClanguageId:
			user_fn = fmgr_dynamic(procedureId, nargs);
			break;
		case POSTQUELlanguageId:
			user_fn = (func_ptr) NULL;
			*nargs = procedureStruct->pronargs;
			break;
		default:
			elog(WARN, "fmgr_info: function %d: unknown language %d",
			     procedureId, language);
		}
	} 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(va_alist)
	va_dcl
{
	va_list		pvar;
	register	i;
	ObjectId	procedureId;
	int 		pronargs;
	FmgrValues	values;
	func_ptr	user_fn;
	Boolean		isNull = false;

	va_start(pvar);
	procedureId = va_arg(pvar, ObjectId);

	fmgr_info(procedureId, &user_fn, &pronargs);

	if (pronargs > MAXFMGRARGS) {
		elog(WARN, "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, (char **) &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...
 */
char *
fmgr_ptr(va_alist)
	va_dcl
{
	va_list		pvar;
	register	i;
	func_ptr	user_fn;
	ObjectId	func_id;
	int		n_arguments;
	FmgrValues	values;
	Boolean		isNull = false;

	va_start(pvar);
	user_fn = va_arg(pvar, func_ptr);
	func_id = va_arg(pvar, ObjectId);
	n_arguments = va_arg(pvar, int);
	if (n_arguments > MAXFMGRARGS) {
		elog(WARN, "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, (char **) &values,
		      &isNull));
}

/*
 * 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(procedureId, nargs, args, isNull)
	ObjectId	procedureId;
	int		nargs;
	char		*args[];
	Boolean		*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, args, isNull));
}

#ifdef WAY_COOL_ORTHOGONAL_FUNCTIONS
char *
postquel_lang_func_call_array(procedureId,pronargs,args)
	ObjectId procedureId;
	int	pronargs;
	char *args[];    
{
	List query_descriptor = LispNil, qd = LispNil;
	HeapTuple   procedureTuple;
	ParamListInfo paramlist;
	char *plan_str;
	int status,x;
	Datum *value;
	Boolean *isnull;


	plan_str = (char *)
		SearchSysCacheGetAttribute(PROOID,Anum_pg_proc_prosrc, procedureId);
	qd = StringToPlanWithParams(textout((struct varlena *)plan_str),&paramlist);
	x=0; 
	while (paramlist[x].kind != PARAM_INVALID) {
		paramlist[x].value = (Datum) args[x];
		x++;
	}
	if (prs2RunOnePlanAndGetValue(qd,paramlist, NULL, &value, &isnull))
		return (char *) value;
	return (char *)NULL;
}

char *
postquel_lang_func_call(procedureId,pronargs,values)
	ObjectId procedureId;
	int	pronargs;
	FmgrValues	values;
{

}

static
ObjectId 
fmgr_func_lang(procedureId)
	ObjectId procedureId;
{
	HeapTuple	procedureTuple;
	ObjectId	language;

	if (fmgr_isbuiltin(procedureId))
		return(INTERNALlanguageId);
	
	procedureTuple = SearchSysCacheTuple(PROOID, (char *) procedureId,
					     NULL, NULL, NULL);
	if (!HeapTupleIsValid(procedureTuple)) {
		elog(WARN, "fmgr_func_lang: function %d: cache lookup failed",
		     procedureId);
		return(InvalidObjectId);
	}
	language = ((struct proc *) GETSTRUCT(procedureTuple))->prolang;
	return(language);
}
/* this goes in all of the interface routines */
{
	language = fmgr_func_lang(procedureId);
	switch (language) {
	case INTERNALlanguageId:
	case ClanguageId:
		return fmgr_c(user_fn, procedureId, pronargs,
			      (char **) &values, &isNull);
		break;
	case POSTQUELlanguageId:
		return postquel_lang_func_call(procedureId,pronargs,values);
		break;
	default:
		elog(WARN, "fmgr: function %d: unknown language %d",
		     procedureId, language);
	}
}
#endif WAY_COOL_ORTHOGONAL_FUNCTIONS
