/* ----------------------------------------------------------------
 *   FILE
 *	fastpath.c
 *	
 *   DESCRIPTION
 * 	routines to handle function requests from the frontend
 *
 *   INTERFACE ROUTINES
 *	
 *   NOTES
 *	this protocol was hacked up quickly - should decide this
 *	and then synchronize code in lib/libpq/{fe,be}-pqexec.c 
 *
 *	error condition may be asserted by fmgr code and
 *   	an "elog" propagated to the frontend independent of the
 *   	code in this module
 *
 *      for no good reason, function names are limited to 80 chars
 *      by this module.
 *	
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/tcop/RCS/fastpath.c,v 1.15 1992/07/13 03:36:48 hong Exp $
 * ----------------------------------------------------------------
 */

#include "tmp/postgres.h"

 RcsId("$Header: /private/postgres/src/tcop/RCS/fastpath.c,v 1.15 1992/07/13 03:36:48 hong Exp $");

/* ----------------
 *	FILE INCLUDE ORDER GUIDELINES
 *
 *	1) tcopdebug.h
 *	2) various support files ("everything else")
 *	3) node files
 *	4) catalog/ files
 *	5) execdefs.h and execmisc.h, if necessary.
 *	6) extern files come last.
 * ----------------
 */
#include "tcop/tcopdebug.h"

#include "utils/palloc.h"
#include "utils/fmgr.h"
#include "utils/log.h"

#include "catalog/syscache.h"
#include "catalog/pg_type.h"

#include "tmp/fastpath.h"

/* ----------------
 *	external_to_internal
 * ----------------
 */
static int
external_to_internal( string )
     char *string;
{
    return((int)string);
}

/* ----------------
 *	internal_to_external
 * ----------------
 */
static char * 
internal_to_external( string )
     char *string;
{
    return(string);
}

/* ----------------
 *	SendFunctionResult
 * ----------------
 */
static void
SendFunctionResult ( fid, retval, rettype )
     int fid;
     char *retval;
     int rettype;
{
    char *retdata;

#if 0    
    printf("Sending results %d, %d, %d",fid,retval,rettype);
#endif    
    
    pq_putnchar("V",1);
    pq_putint(fid,4);
    
    switch (rettype) {

      case 0: 			/* void retval */
        break;
      case PORTAL_RESULT:
	break;

      case VAR_LENGTH_RESULT:
	retdata = internal_to_external(retval, MAX_STRING_LENGTH, 0);
	pq_putnchar("G", 1);
	pq_putint(rettype, 4);
	pq_putstr(retdata);
	break;

      case 1: case 2: case 4:
	pq_putnchar("G",1);
	pq_putint(rettype,4);
	pq_putint(retval, rettype);
	break;

      default:
	pq_putnchar("G",1);
	pq_putint(VARSIZE(retval)-4,4);
	pq_putnchar(retval+4, VARSIZE(retval)-4);
    }
    pq_putnchar("0", 1);
    pq_flush();
} /* SendFunctionResult */

/* 
    {
	Datum sendproc;
	bool attr_is_null;

	tuple = SearchSysCacheTuple (TYPOID,rettype);
	if (HeapTupleIsValid(tuple)) {


	    sendproc = HeapTupleGetAttributeValue(tuple,
						  InvalidBuffer,
						  13,
						  tupledesc,
						  attr_is_null);
						  
	    if ( RegProcedureIsValid ( sendproc )  == false )
	      elog(WARN,"invalid sending procedure");

    } */

/* ---------------
 * 
 * HandleFunctionRequest
 * - externally callable routine for handling function requests
 * 
 * Desc : This code is called only when the backend recieves
 *        a function request (current protocol is "F") we then
 *        handle the parameters of the request here.
 *        Requests come in 2 forms, by name, and by id.
 *        If requests come by id, we just call the fmgr with the id
 *        and the required number of arguments.
 *        If requests come by name, we do a catalog lookup to determine
 *        the id, which we subsequently return to the frontend so that
 *        it may cache it , and subsequent calls to the same function
 *        can be made by id.
 *
 *        XXX - note that if the catalog changes in the meantime, 
 *              the function id cannot be invalidated here, and the
 *              frontend must take care of it by itself  ( - jeff)
 *	  XXX - someday, maybe we should standardize on an RPC 
 *		mechanism that everyone else uses
 *
 *        If any error condition exists, we terminate with an elog
 *        which causes an "E" followed by the message to be sent to the
 *        frontend.  
 *
 * BUGS:  for no particular reasion, function names are limited to 80
 *        characters, and VARLENGTHARGS has a max of 100 (inherited from
 *	  the lisp backend)
 * CAVEAT:the function_by_name code was given to me by Mike H, I am taking
 *        it on faith since it looks ok & I don't have the time to verify it. 
 *
 * ----------------
 */

int
HandleFunctionRequest()
{
    int xactid;
    int fid;
    char function_name[MAX_FUNC_NAME_LENGTH+1];
    char *retval;		/* XXX - should be datum, maybe ? */
    int  arg[8];
    int  arg_length[8];
    int  rettype;
    int  nargs;
    int  i;
    unsigned char palloced;

    xactid = pq_getint(4);
    fid = pq_getint(4);
    rettype = pq_getint(4);
    nargs = pq_getint(4);

    /*
     *  Copy arguments into arg vector.  If we palloc() an argument, we need
     *  to remember, so that we pfree() it after the call.  Elsewhere, we
     *  hardwire nargs <= 8, so palloced is an eight-bit flag vector.
     */

    palloced = 0x0;
    for (i = 0 ; i < nargs ; i++ ) {
	arg_length[i] = pq_getint(4);
	
	if (arg_length[i] == VAR_LENGTH_ARG ) {
	    char *data = (char *) palloc(MAX_STRING_LENGTH);
	    palloced |= (1 << i);
	    pq_getstr(data,MAX_STRING_LENGTH);
	    arg[i] = external_to_internal(data,MAX_STRING_LENGTH,PASS_BY_REF);
	} else if (arg_length[i] > 4)  {
	    char *vl;
/*	    elog(WARN,"arg_length of %dth argument too long",i);*/
	    vl = palloc(arg_length[i]);
	    palloced |= (1 << i);
	    VARSIZE(vl) = arg_length[i]+4;
	    pq_getnchar(VARDATA(vl),0,arg_length[i]);
	    arg[i] = (int)vl;
	} else {
	    arg[i] = pq_getint ( arg_length[i]);
	}
    }

    /* lookup rettype in type catalog, and check send function */

    if (rettype == PORTAL_RESULT) {
	/* fake it out by putting the stuff out first */
	pq_putnchar("V",1);
	pq_putint(0,4);
    }

#if 0    
    printf("\n arguments are %d %d %d \n",fid,arg[0],arg[1]);
    printf("rettype = %d\n",rettype);
#endif 
    
    retval = fmgr (fid, arg[0],arg[1],arg[2],arg[3],
		   arg[4],arg[5],arg[6],arg[7] );

    for (i = 0; i < nargs; i++) {
	if (palloced & (1 << i))
	    pfree((Pointer)arg[i]);
    }

    if (rettype != PORTAL_RESULT)
	SendFunctionResult ( fid, retval, rettype );
}
