/*-------------------------------------------------------------------------
 *
 * be-pqexec.c--
 *	  support for executing POSTGRES commands and functions from a
 *	  user-defined function in a backend.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /usr/local/cvsroot/pgsql/src/backend/libpq/be-pqexec.c,v 1.7 1997/09/08 21:43:43 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
 *		PQfn			- call a POSTGRES function
 *		PQexec			- execute a POSTGRES query
 *
 * NOTES
 *		These routines are compiled into the postgres backend.
 */
#include <postgres.h>

#include <nodes/pg_list.h>
#include <tcop/dest.h>
#include <tcop/fastpath.h>
#include <tcop/tcopprot.h>
#include <lib/dllist.h>
#include <libpq/libpq-be.h>
#include <fmgr.h>
#include <utils/exc.h>
#include <utils/builtins.h>
#ifndef HAVE_MEMMOVE
#include <regex/utils.h>
#else
#include <string.h>
#endif

static char *strmake(char *str, int len);

/* ----------------------------------------------------------------
 *						PQ interface routines
 * ----------------------------------------------------------------
 */

/* ----------------
 *		PQfn -	Send a function call to the POSTGRES backend.
 *
 *		fnid			: function id
 *		result_buf		: pointer to result buffer (&int if integer)
 *		result_len		: length of return value.
 *		result_is_int	: If the result is an integer, this must be non-zero
 *		args			: pointer to a NULL terminated arg array.
 *						  (length, if integer, and result-pointer)
 *		nargs			: # of arguments in args array.
 *
 *		This code scavanged from HandleFunctionRequest() in tcop/fastpath.h
 * ----------------
 */
char	   *
PQfn(int fnid,
	 int *result_buf,			/* can't use void, dec compiler barfs */
	 int result_len,
	 int result_is_int,
	 PQArgBlock *args,
	 int nargs)
{
	char	   *retval;			/* XXX - should be datum, maybe ? */
	char	   *arg[8];
	int			i;

	/* ----------------
	 *	fill args[] array
	 * ----------------
	 */
	for (i = 0; i < nargs; i++)
	{
		if (args[i].len == VAR_LENGTH_ARG)
		{
			arg[i] = (char *) args[i].u.ptr;
		}
		else if (args[i].len > 4)
		{
			elog(WARN, "arg_length of argument %d too long", i);
		}
		else
		{
			arg[i] = (char *) args[i].u.integer;
		}
	}

	/* ----------------
	 *	call the postgres function manager
	 * ----------------
	 */
	retval = (char *)
		fmgr(fnid, arg[0], arg[1], arg[2], arg[3],
			 arg[4], arg[5], arg[6], arg[7]);

	/* ----------------
	 *	put the result in the buffer the user specified and
	 *	return the proper code.
	 * ----------------
	 */
	if (retval == (char *) NULL)/* void retval */
		return "0";

	if (result_is_int)
	{
		*result_buf = (int) retval;
	}
	else
	{
		memmove(result_buf, retval, result_len);
	}
	return "G";
}

/* ----------------
 *		PQexec -  Send a query to the POSTGRES backend
 *
 *		The return value is a string.
 *		If 0 or more tuples fetched from the backend, return "P portal-name".
 *		If a query is does not return tuples, return "C query-command".
 *		If there is an error: return "E error-message".
 *
 *		Note: if we get a serious error or an elog(WARN), then PQexec never
 *		returns because the system longjmp's back to the main loop.
 * ----------------
 */
char	   *
PQexec(char *query)
{
	PortalEntry *entry = NULL;
	char	   *result = NULL;

	/* ----------------
	 *	create a new portal and put it on top of the portal stack.
	 * ----------------
	 */
	entry = (PortalEntry *) be_newportal();
	be_portalpush(entry);

	/* ----------------
	 *	pg_eval_dest will put the query results in a portal which will
	 *	end up on the top of the portal stack.
	 * ----------------
	 */
	pg_eval_dest(query, (char **) NULL, (Oid *) NULL, 0, Local);

	/* ----------------
	 *	pop the portal off the portal stack and return the
	 *	result.  Note if result is null, we return C.
	 * ----------------
	 */
	entry = (PortalEntry *) be_portalpop();
	result = entry->result;
	if (result == NULL)
	{
		char	   *PQE = "Cnull PQexec result";

		result = pstrdup(PQE);
	}

	if (result[0] != 'P')
	{

		/*
		 * some successful command was executed, but it's not one where we
		 * return the portal name so here we should be sure to clear out
		 * the portal (since the caller has no handle on it)
		 */
		pbuf_close(entry->name);

	}
	return result;
}

/* ----------------------------------------------------------------
 *						pqtest support
 * ----------------------------------------------------------------
 */

/* ----------------
 *		pqtest_PQexec takes a text query and returns the number of
 *		tuples it returns.	Note: there is no need to PQclear()
 *		here - the memory will go away at end transaction.
 * ----------------
 */
int
pqtest_PQexec(char *q)
{
	PortalBuffer *a;
	char	   *res;
	int			t;

	/* ----------------
	 *	execute the postgres query
	 * ----------------
	 */
	res = PQexec(q);

	/* ----------------
	 *	return number of tuples in portal or 0 if command returns no tuples.
	 * ----------------
	 */
	t = 0;
	switch (res[0])
	{
		case 'P':
			a = PQparray(&res[1]);
			if (a == NULL)
				elog(WARN, "pqtest_PQexec: PQparray could not find portal %s",
					 res);

			t = PQntuples(a);
			break;
		case 'C':
			break;
		default:
			elog(NOTICE, "pqtest_PQexec: PQexec(%s) returns %s", q, res);
			break;
	}

	return t;
}

/* ----------------
 *		utilities for pqtest_PQfn()
 * ----------------
 */
static char *
strmake(char *str, int len)
{
	char	   *newstr;

	if (str == NULL)
		return NULL;
	if (len <= 0)
		len = strlen(str);

	newstr = (char *) palloc((unsigned) len + 1);
	strNcpy(newstr, str, len);
	newstr[len] = (char) 0;
	return newstr;
}

#define SKIP 0
#define SCAN 1

static char spacestr[] = " ";

static int
strparse(char *s, char **fields, int *offsets, int maxfields)
{
	int			len = strlen(s);
	char	   *cp = s,
			   *end = cp + len,
			   *ep;
	int			parsed = 0;
	int			mode = SKIP,
				i = 0;

	if (*(end - 1) == '\n')
		end--;

	for (i = 0; i < maxfields; i++)
		fields[i] = spacestr;

	i = 0;
	while (!parsed)
	{
		if (mode == SKIP)
		{

			while ((cp < end) &&
				   (*cp == ' ' || *cp == '\t'))
				cp++;
			if (cp < end)
				mode = SCAN;
			else
				parsed = 1;

		}
		else
		{

			ep = cp;
			while ((ep < end) && (*ep != ' ' && *ep != '\t'))
				ep++;

			if (ep < end)
				mode = SKIP;
			else
				parsed = 1;

			fields[i] = strmake(cp, ep - cp);
			if (offsets != NULL)
				offsets[i] = cp - s;

			i++;
			cp = ep;
			if (i > maxfields)
				parsed = 1;

		}
	}
	return i;
}

/* ----------------
 *		pqtest_PQfn converts it's string into a PQArgBlock and
 *		calls the specified function, which is assumed to return
 *		an integer value.
 * ----------------
 */
int
pqtest_PQfn(char *q)
{
	int			k,
				j,
				i,
				v,
				f,
				offsets;
	char	   *fields[8];
	PQArgBlock	pqargs[7];
	int			res;
	char	   *pqres;

	/* ----------------
	 *	parse q into fields
	 * ----------------
	 */
	i = strparse(q, fields, &offsets, 8);
	printf("pqtest_PQfn: strparse returns %d fields\n", i);		/* debug */
	if (i == 0)
		return -1;

	/* ----------------
	 *	get the function id
	 * ----------------
	 */
	f = atoi(fields[0]);
	printf("pqtest_PQfn: func is %d\n", f);		/* debug */
	if (f == 0)
		return -1;

	/* ----------------
	 *	build a PQArgBlock
	 * ----------------
	 */
	for (j = 1; j < i && j < 8; j++)
	{
		k = j - 1;
		v = atoi(fields[j]);
		if (v != 0 || (v == 0 && fields[j][0] == '0'))
		{
			pqargs[k].len = 4;
			pqargs[k].u.integer = v;
			printf("pqtest_PQfn: arg %d is int %d\n", k, v);	/* debug */
		}
		else
		{
			pqargs[k].len = VAR_LENGTH_ARG;
			pqargs[k].u.ptr = (int *) textin(fields[j]);
			printf("pqtest_PQfn: arg %d is text %s\n", k, fields[j]);	/* debug */
		}
	}

	/* ----------------
	 *	call PQfn
	 * ----------------
	 */
	pqres = PQfn(f, &res, 4, 1, pqargs, i - 1);
	printf("pqtest_PQfn: pqres is %s\n", pqres);		/* debug */

	/* ----------------
	 *	free memory used
	 * ----------------
	 */
	for (j = 0; j < i; j++)
	{
		pfree(fields[j]);
		if (pqargs[j].len == VAR_LENGTH_ARG)
			pfree(pqargs[j].u.ptr);
	}

	/* ----------------
	 *	return result
	 * ----------------
	 */
	printf("pqtest_PQfn: res is %d\n", res);	/* debugg */
	return res;
}

/* ----------------
 *		pqtest looks at the first character of it's test argument
 *		and decides which of pqtest_PQexec or pqtest_PQfn to call.
 * ----------------
 */
int32
pqtest(struct varlena * vlena)
{
	char	   *q;

	/* ----------------
	 *	get the query
	 * ----------------
	 */
	q = textout(vlena);
	if (q == NULL)
		return -1;

	switch (q[0])
	{
		case '%':
			return pqtest_PQfn(&q[1]);
			break;
		default:
			return pqtest_PQexec(q);
			break;
	}
	return (0);
}
