/*-------------------------------------------------------------------------
 *
 * portal.c
 *	  generalized portal support routines
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *	$Id: portal.c,v 1.22 1999/05/25 16:09:01 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
 *	 INTERFACE ROUTINES
 *		PQnportals		- Return the number of open portals.
 *		PQpnames		- Return all the portal names
 *		PQparray		- Return the portal buffer given a portal name
 *		PQrulep			- Return 1 if an asynchronous portal
 *		PQntuples		- Return the number of tuples in a portal buffer
 *		PQninstances	-	same as PQntuples using object terminology
 *		PQngroups		- Return the number of tuple groups in a portal buffer
 *		PQntuplesGroup	- Return the number of tuples in a tuple group
 *		PQninstancesGroup  - same as PQntuplesGroup using object terminology
 *		PQnfieldsGroup	- Return the number of fields in a tuple group
 *		PQfnumberGroup	- Return field number given (group index, field name)
 *		PQftypeGroup	- Return field type given (group index, field index)
 *		PQfsizeGroup	- Return field size given (group index, field index)
 *		PQfnameGroup	- Return field name given (group index, field index)
 *		PQgroup			- Return the tuple group that a particular tuple is in
 *		PQgetgroup		- Return the index of the group that a tuple is in
 *		PQnfields		- Return the number of fields in a tuple
 *		PQfnumber		- Return the field index of a field name in a tuple
 *		PQfname			- Return the name of a field
 *		PQftype			- Return the type of a field
 *		PQfsize			- Return the size of a field
 *		PQftype			- Return the type of a field
 *		PQsametype		- Return 1 if the two tuples have the same type
 *		PQgetvalue		- Return an attribute (field) value
 *		PQgetlength		- Return an attribute (field) length
 *		PQclear			- free storage claimed by named portal
 *
 *	 NOTES
 *		These functions may be used by both frontend routines which
 *		communicate with a backend or by user-defined functions which
 *		are compiled or dynamically loaded into a backend.
 *
 *		the *portals array should be organized as a hash table for
 *		quick portal-by-name lookup.
 *
 *		Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal"
 *		see utils/mmgr/portalmem.c for why. -cim 2/22/91
 *
 */
#include <stdio.h>
#include <string.h>

#include <postgres.h>

#include <lib/dllist.h>
#include <libpq/libpq.h>		/* where the declarations go */
#include <utils/exc.h>


/* ----------------------------------------------------------------
 *	  Helper routines for PQ portal interface routines below
 * ----------------------------------------------------------------
 */

static int
in_range(char *msg, int value, int min, int max)
{
	if (value < min || value >= max)
	{
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
		"FATAL: %s, %d is not in range [%d,%d)\n", msg, value, min, max);
		pqdebug("%s", PQerrormsg);
		fputs(PQerrormsg, stderr);
		return 0;
	}
	return 1;
}

static int
valid_pointer(char *msg, void *ptr)
{
	if (!ptr)
	{
		snprintf(PQerrormsg, ERROR_MSG_LENGTH, "FATAL: %s\n", msg);
		pqdebug("%s", PQerrormsg);
		fputs(PQerrormsg, stderr);
		return 0;
	}
	return 1;
}


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

/* --------------------------------
 *		PQnportals - Return the number of open portals.
 *		If rule_p, only return asynchronous portals.
 * --------------------------------
 */
int
PQnportals(int rule_p)
{
	int			i,
				n = 0;

	for (i = 0; i < portals_array_size; ++i)
	{
		if (portals[i] && portals[i]->portal)
		{
			if (!rule_p || portals[i]->portal->rule_p)
				++n;
		}
	}
	return n;
}

/* --------------------------------
 *		PQpnames - Return all the portal names
 *		If rule_p, only return asynchronous portals.
 *
 *		   the caller must have allocated sufficient memory for char** pnames
 *		   (an array of PQnportals strings of length PortalNameLength).
 *
 *		   notice that this assumes that the user is calling PQnportals and
 *		   PQpnames with the same rule_p argument, and with no intervening
 *		   portal closures.  if not, you can get in heap big trouble..
 * --------------------------------
 */
void
PQpnames(char **pnames, int rule_p)
{
	int			i,
				cur_pname = 0;

	if (!valid_pointer("PQpnames: invalid name buffer", pnames))
		return;

	for (i = 0; i < portals_array_size; ++i)
	{
		if (portals[i] && portals[i]->portal)
		{
			if (!rule_p || portals[i]->portal->rule_p)
			{
				strncpy(pnames[cur_pname], portals[i]->name, PortalNameLength + 1);
				++cur_pname;
			}
		}
	}
}

/* --------------------------------
 *		PQparray - Return the portal buffer given a portal name
 * --------------------------------
 */
PortalBuffer *
PQparray(char *pname)
{
	int			i;

	if (!valid_pointer("PQparray: invalid name buffer", pname))
		return NULL;

	if ((i = pbuf_getIndex(pname)) < 0)
		return (PortalBuffer *) NULL;
	return portals[i]->portal;
}

/* --------------------------------
 *		PQrulep - Return 1 if an asynchronous portal
 * --------------------------------
 */
int
PQrulep(PortalBuffer *portal)
{
	if (!valid_pointer("PQrulep: invalid portal pointer", portal))
		return -1;

	return portal->rule_p;
}

/* --------------------------------
 *		PQntuples - Return the number of tuples in a portal buffer
 * --------------------------------
 */
int
PQntuples(PortalBuffer *portal)
{
	if (!valid_pointer("PQntuples: invalid portal pointer", portal))
		return -1;

	return portal->no_tuples;
}

int
PQninstances(PortalBuffer *portal)
{
	return PQntuples(portal);
}

/* --------------------------------
 *		PQngroups - Return the number of tuple groups in a portal buffer
 * --------------------------------
 */
int
PQngroups(PortalBuffer *portal)
{
	if (!valid_pointer("PQngroups: invalid portal pointer", portal))
		return -1;

	return portal->no_groups;
}

/* --------------------------------
 *		PQntuplesGroup - Return the number of tuples in a tuple group
 * --------------------------------
 */
int
PQntuplesGroup(PortalBuffer *portal, int group_index)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQntuplesGroup: invalid portal pointer", portal) ||
		!in_range("PQntuplesGroup: group index",
				  group_index, 0, portal->no_groups))
		return -1;

	gbp = pbuf_findGroup(portal, group_index);
	if (gbp)
		return gbp->no_tuples;
	return -1;
}

int
PQninstancesGroup(PortalBuffer *portal, int group_index)
{
	return PQntuplesGroup(portal, group_index);
}

/* --------------------------------
 *		PQnfieldsGroup - Return the number of fields in a tuple group
 * --------------------------------
 */
int
PQnfieldsGroup(PortalBuffer *portal, int group_index)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQnfieldsGroup: invalid portal pointer", portal) ||
		!in_range("PQnfieldsGroup: group index",
				  group_index, 0, portal->no_groups))
		return -1;
	gbp = pbuf_findGroup(portal, group_index);
	if (gbp)
		return gbp->no_fields;
	return -1;
}

/* --------------------------------
 *		PQfnumberGroup - Return the field number (index) given
 *						 the group index and the field name
 * --------------------------------
 */
int
PQfnumberGroup(PortalBuffer *portal, int group_index, char *field_name)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQfnumberGroup: invalid portal pointer", portal) ||
		!valid_pointer("PQfnumberGroup: invalid field name pointer",
					   field_name) ||
		!in_range("PQfnumberGroup: group index",
				  group_index, 0, portal->no_groups))
		return -1;
	gbp = pbuf_findGroup(portal, group_index);
	if (gbp)
		return pbuf_findFnumber(gbp, field_name);
	return -1;
}

/* --------------------------------
 *		PQfnameGroup - Return the field (attribute) name given
 *						the group index and field index.
 * --------------------------------
 */
char *
PQfnameGroup(PortalBuffer *portal, int group_index, int field_number)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQfnameGroup: invalid portal pointer", portal) ||
		!in_range("PQfnameGroup: group index",
				  group_index, 0, portal->no_groups))
		return (char *) NULL;

	if ((gbp = pbuf_findGroup(portal, group_index)) &&
		in_range("PQfnameGroup: field number",
				 field_number, 0, gbp->no_fields))
		return pbuf_findFname(gbp, field_number);
	return (char *) NULL;
}

/* --------------------------------
 *		PQftypeGroup - Return the type of a field given
 *						the group index and field index
 * --------------------------------
 */
int
PQftypeGroup(PortalBuffer *portal, int group_index, int field_number)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQftypeGroup: invalid portal pointer", portal) ||
		!in_range("PQftypeGroup: group index",
				  group_index, 0, portal->no_groups))
		return -1;

	if ((gbp = pbuf_findGroup(portal, group_index)) &&
		in_range("PQftypeGroup: field number", field_number, 0, gbp->no_fields))
		return gbp->types[field_number].typid;
	return -1;
}

/* --------------------------------
 *		PQfsizeGroup - Return the size of a field given
 *					   the group index and field index
 * --------------------------------
 */
int
PQfsizeGroup(PortalBuffer *portal, int group_index, int field_number)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQfsizeGroup: invalid portal pointer", portal) ||
		!in_range("PQfsizeGroup: tuple index",
				  group_index, 0, portal->no_groups))
		return -1;

	if ((gbp = pbuf_findGroup(portal, group_index)) &&
		in_range("PQfsizeGroup: field number", field_number, 0, gbp->no_fields))
		return gbp->types[field_number].typlen;
	return -1;
}


/* --------------------------------
 *		PQgroup - Return the tuple group that a particular tuple is in
 * --------------------------------
 */
GroupBuffer *
PQgroup(PortalBuffer *portal, int tuple_index)
{
	GroupBuffer *gbp;
	int			tuple_count = 0;

	if (!valid_pointer("PQgroup: invalid portal pointer", portal) ||
		!in_range("PQgroup: tuple index",
				  tuple_index, 0, portal->no_tuples))
		return (GroupBuffer *) NULL;

	for (gbp = portal->groups;
		 gbp && tuple_index >= (tuple_count += gbp->no_tuples);
		 gbp = gbp->next)
		;
	if (!in_range("PQgroup: tuple not found: tuple index",
				  tuple_index, 0, tuple_count))
		return (GroupBuffer *) NULL;
	return gbp;
}

/* --------------------------------
 *		PQgetgroup - Return the index of the group that a
 *					 particular tuple is in
 * --------------------------------
 */
int
PQgetgroup(PortalBuffer *portal, int tuple_index)
{
	GroupBuffer *gbp;
	int			tuple_count = 0,
				group_count = 0;

	if (!valid_pointer("PQgetgroup: invalid portal pointer", portal) ||
		!in_range("PQgetgroup: tuple index",
				  tuple_index, 0, portal->no_tuples))
		return -1;

	for (gbp = portal->groups;
		 gbp && tuple_index >= (tuple_count += gbp->no_tuples);
		 gbp = gbp->next)
		++group_count;
	if (!gbp || !in_range("PQgetgroup: tuple not found: tuple index",
						  tuple_index, 0, tuple_count))
		return -1;
	return group_count;
}

/* --------------------------------
 *		PQnfields - Return the number of fields in a tuple
 * --------------------------------
 */
int
PQnfields(PortalBuffer *portal, int tuple_index)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQnfields: invalid portal pointer", portal) ||
		!in_range("PQnfields: tuple index",
				  tuple_index, 0, portal->no_tuples))
		return -1;
	gbp = PQgroup(portal, tuple_index);
	if (gbp)
		return gbp->no_fields;
	return -1;
}

/* --------------------------------
 *		PQfnumber - Return the field index of a given
 *					field name within a tuple.
 * --------------------------------
 */
int
PQfnumber(PortalBuffer *portal, int tuple_index, char *field_name)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQfnumber: invalid portal pointer", portal) ||
	!valid_pointer("PQfnumber: invalid field name pointer", field_name) ||
		!in_range("PQfnumber: tuple index",
				  tuple_index, 0, portal->no_tuples))
		return -1;
	gbp = PQgroup(portal, tuple_index);
	if (gbp)
		return pbuf_findFnumber(gbp, field_name);
	return -1;
}

/* --------------------------------
 *		PQfname - Return the name of a field
 * --------------------------------
 */
char *
PQfname(PortalBuffer *portal, int tuple_index, int field_number)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQfname: invalid portal pointer", portal) ||
		!in_range("PQfname: tuple index",
				  tuple_index, 0, portal->no_tuples))
		return (char *) NULL;

	if ((gbp = PQgroup(portal, tuple_index)) &&
		in_range("PQfname: field number",
				 field_number, 0, gbp->no_fields))
		return pbuf_findFname(gbp, field_number);
	return (char *) NULL;
}

/* --------------------------------
 *		PQftype - Return the type of a field
 * --------------------------------
 */
int
PQftype(PortalBuffer *portal, int tuple_index, int field_number)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQftype: invalid portal pointer", portal) ||
		!in_range("PQfname: tuple index",
				  tuple_index, 0, portal->no_tuples))
		return -1;

	if ((gbp = PQgroup(portal, tuple_index)) &&
	  in_range("PQftype: field number", field_number, 0, gbp->no_fields))
		return gbp->types[field_number].typid;
	return -1;
}

/* --------------------------------
 *		PQfsize - Return the size of a field
 * --------------------------------
 */
int
PQfsize(PortalBuffer *portal, int tuple_index, int field_number)
{
	GroupBuffer *gbp;

	if (!valid_pointer("PQfsize: invalid portal pointer", portal) ||
		!in_range("PQfsize: tuple index",
				  tuple_index, 0, portal->no_tuples))
		return -1;

	if ((gbp = PQgroup(portal, tuple_index)) &&
	  in_range("PQfsize: field number", field_number, 0, gbp->no_fields))
		return gbp->types[field_number].typlen;
	return -1;
}



/* --------------------------------
 *		PQsametype - Return 1 if the two tuples have the same type
 *						(in the same group)
 * --------------------------------
 */
int
PQsametype(PortalBuffer *portal, int tuple_index1, int tuple_index2)
{
	GroupBuffer *gbp1,
			   *gbp2;

	if (!valid_pointer("PQsametype: invalid portal pointer", portal) ||
		!in_range("PQsametype: tuple index 1",
				  tuple_index1, 0, portal->no_tuples) ||
		!in_range("PQsametype: tuple index 2",
				  tuple_index2, 0, portal->no_tuples))
		return -1;

	gbp1 = PQgroup(portal, tuple_index1);
	gbp2 = PQgroup(portal, tuple_index2);
	if (gbp1 && gbp2)
		return gbp1 == gbp2;
	return -1;
}

static TupleBlock *
PQGetTupleBlock(PortalBuffer *portal,
				int tuple_index,
				int *tuple_offset)
{
	GroupBuffer *gbp;
	TupleBlock *tbp;
	int			tuple_count = 0;

	if (!valid_pointer("PQGetTupleBlock: invalid portal pointer", portal) ||
		!valid_pointer("PQGetTupleBlock: invalid offset pointer",
					   tuple_offset) ||
		!in_range("PQGetTupleBlock: tuple index",
				  tuple_index, 0, portal->no_tuples))
		return (TupleBlock *) NULL;

	for (gbp = portal->groups;
		 gbp && tuple_index >= (tuple_count += gbp->no_tuples);
		 gbp = gbp->next)
		;
	if (!gbp ||
		!in_range("PQGetTupleBlock: tuple not found: tuple index",
				  tuple_index, 0, tuple_count))
		return (TupleBlock *) NULL;
	tuple_count -= gbp->no_tuples;
	for (tbp = gbp->tuples;
		 tbp && tuple_index >= (tuple_count += TupleBlockSize);
		 tbp = tbp->next)
		;
	if (!tbp ||
		!in_range("PQGetTupleBlock: tuple not found: tuple index",
				  tuple_index, 0, tuple_count))
		return (TupleBlock *) NULL;
	tuple_count -= TupleBlockSize;

	*tuple_offset = tuple_index - tuple_count;
	return tbp;
}

/* --------------------------------
 *		PQgetvalue - Return an attribute (field) value
 * --------------------------------
 */
char *
PQgetvalue(PortalBuffer *portal,
		   int tuple_index,
		   int field_number)
{
	TupleBlock *tbp;
	int			tuple_offset;

	tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset);
	if (tbp)
		return tbp->values[tuple_offset][field_number];
	return (char *) NULL;
}

/* --------------------------------
 *		PQgetAttr - Return an attribute (field) value
 *		this differs from PQgetvalue in that the value returned is
 *		a copy.  The CALLER is responsible for free'ing the data returned.
 * --------------------------------
 */
char *
PQgetAttr(PortalBuffer *portal,
		  int tuple_index,
		  int field_number)
{
	TupleBlock *tbp;
	int			tuple_offset;
	int			len;
	char	   *result = NULL;

	tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset);
	if (tbp)
	{
		len = tbp->lengths[tuple_offset][field_number];
		result = palloc(len + 1);
		memcpy(result,
			   tbp->values[tuple_offset][field_number],
			   len);
		result[len] = '\0';
	}
	return result;
}


/* --------------------------------
 *		PQgetlength - Return an attribute (field) length
 * --------------------------------
 */
int
PQgetlength(PortalBuffer *portal,
			int tuple_index,
			int field_number)
{
	TupleBlock *tbp;
	int			tuple_offset;

	tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset);
	if (tbp)
		return tbp->lengths[tuple_offset][field_number];
	return -1;
}

/* ----------------
 *		PQclear			- free storage claimed by named portal
 * ----------------
 */
void
PQclear(char *pname)
{
	if (!valid_pointer("PQclear: invalid portal name pointer", pname))
		return;
	pbuf_close(pname);
}
