/*-------------------------------------------------------------------------
 *
 * portalbuf.c
 *	  portal buffer support routines for src/libpq/portal.c
 *
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /home/projects/pgsql/cvsroot/pgsql/src/backend/libpq/portalbuf.c,v 1.24 2000/04/12 17:15:14 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
 *		pbuf_alloc		  - allocate memory for libpq routines
 *		pbuf_free		  - free memory for libpq routines
 *		pbuf_addPortal	  - Allocate a new portal buffer
 *		pbuf_addGroup	  - Add a new tuple group to the portal
 *		pbuf_addTypes	  - Allocate n type blocks
 *		pbuf_addTuples	  - Allocate a tuple block
 *		pbuf_addTuple	  - Allocate a tuple of n fields (attributes)
 *		pbuf_addValues	  - Allocate n bytes for a value
 *		pbuf_addEntry	  - Allocate a portal entry
 *		pbuf_freeEntry	  - Free a portal entry in the portal table
 *		pbuf_freeTypes	  - Free up the space used by a portal
 *		pbuf_freeTuples   - free space used by tuple block
 *		pbuf_freeGroup	  - free space used by group, types and tuples
 *		pbuf_freePortal   - free space used by portal and portal's group
 *		pbuf_getIndex	  - Return the index of the portal entry
 *		pbuf_setup		  - Set up a portal for dumping data
 *		pbuf_close		  - Close a portal, remove it from the portal table
 *		pbuf_findGroup	  - Return group given the group_index
 *		pbuf_findFnumber  - Return field index of a given field within a group
 *		pbuf_findFname	  - Find the field name given the field index
 *		pbuf_checkFnumber - signal an error if field number is out of bounds
 *
 * 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 <sys/types.h>

#include "postgres.h"

#include "libpq/libpq.h"

PortalEntry **portals = (PortalEntry **) NULL;
size_t		portals_array_size = 0;

/* portals array memory is palloc'd instead of using MemoryContexts */
/* since it will be used by both front and backend programs*/
/*	GlobalMemory portals_mmcxt = (GlobalMemory) NULL;  */

/* -------------------------------
 * portals_realloc
 *	  grow the size of the portals array by size
 *
 *	  also ensures that elements are initially NULL
 */

static void
portals_realloc(size_t size)
{
	size_t		oldsize;
	int			i;
	PortalEntry **newp;

	oldsize = portals_array_size;

	portals_array_size += size;
	if (portals)
		newp = (PortalEntry **) realloc(portals,
							 portals_array_size * sizeof(PortalEntry *));
	else
		newp = (PortalEntry **) palloc(portals_array_size * sizeof(PortalEntry *));

	if (newp)
		portals = newp;
	else
		libpq_raise(&PortalError,
		   vararg_format("Cannot alloc more memory in portals_realloc"));

	for (i = oldsize; i < (int) portals_array_size; i++)
		portals[i] = (PortalEntry *) NULL;

}

/* --------------------------------
 *		pbuf_alloc - allocate memory for portal buffers
 *
 *		remember: palloc() in the backend uses the postgres MemoryContext
 *		library and palloc() in the frontend (fe-pqstubs.c) calls malloc().
 * --------------------------------
 */
caddr_t
pbuf_alloc(size_t size)
{
	caddr_t		addr;

	if (size <= 0)
		libpq_raise(&MemoryError, vararg_format("Invalid argument to pbuf_alloc()."));

	addr = (caddr_t) palloc(size);
	if (addr == (caddr_t) NULL)
		libpq_raise(&MemoryError, vararg_format("Cannot Allocate space."));

	return addr;
}

/* --------------------------------
 *		pbuf_free - free memory for portal buffers
 *
 *		remember: pfree() in the backend uses the postgres MemoryContext
 *		library and pfree() in the frontend (fe-pqstubs.c) calls free().
 * --------------------------------
 */
void
pbuf_free(caddr_t pointer)
{
	if (pointer)
		pfree(pointer);
	else
		libpq_raise(&MemoryError, vararg_format("Tried to free NULL memory pointer"));

}

/* --------------------------------
 *		pbuf_addPortal - Allocate a new portal buffer
 * --------------------------------
 */
PortalBuffer *
pbuf_addPortal()
{
	PortalBuffer *portal;

	portal = (PortalBuffer *)
		pbuf_alloc(sizeof(PortalBuffer));

	portal->rule_p = 0;
	portal->no_tuples = 0;
	portal->no_groups = 0;
	portal->groups = NULL;

	return portal;
}

/* --------------------------------
 *		pbuf_addGroup - Add a new tuple group to the portal
 * --------------------------------
 */
GroupBuffer *
pbuf_addGroup(PortalBuffer *portal)
{
	GroupBuffer *group,
			   *group1;

	group = (GroupBuffer *)
		pbuf_alloc(sizeof(GroupBuffer));

	/* Initialize the new group buffer. */
	group->no_tuples = 0;
	group->no_fields = 0;
	group->types = NULL;
	group->tuples = NULL;
	group->next = NULL;

	if ((group1 = portal->groups) == NULL)
		portal->groups = group;
	else
	{
		while (group1->next != NULL)
			group1 = group1->next;
		group1->next = group;
	}

	return group;
}

/* --------------------------------
 *		pbuf_addTypes - Allocate n type blocks
 * --------------------------------
 */
TypeBlock  *
pbuf_addTypes(int n)
{
	TypeBlock  *types;

	types = (TypeBlock *)
		pbuf_alloc(n * sizeof(TypeBlock));

	return types;
}

/* --------------------------------
 *		pbuf_addTuples - Allocate a tuple block
 * --------------------------------
 */
TupleBlock *
pbuf_addTuples()
{
	TupleBlock *tuples;

	tuples = (TupleBlock *)
		pbuf_alloc(sizeof(TupleBlock));

	tuples->next = NULL;
	tuples->tuple_index = 0;

	return tuples;
}

/* --------------------------------
 *		pbuf_addTuple - Allocate a tuple of n fields (attributes)
 * --------------------------------
 */
char	  **
pbuf_addTuple(int n)
{
	return (char **)
	pbuf_alloc(n * sizeof(char *));
}

/* --------------------------------
 *		pbuf_addTupleValueLengths - Allocate a tuple of n lengths (attributes)
 * --------------------------------
 */
int *
pbuf_addTupleValueLengths(int n)
{
	return (int *)
	pbuf_alloc(n * sizeof(int));
}

/* --------------------------------
 *		pbuf_addValues - Allocate n bytes for a value
 * --------------------------------
 */
char *
pbuf_addValues(int n)
{
	return pbuf_alloc(n);
}

/* --------------------------------
 *		pbuf_addEntry - Allocate a portal entry
 * --------------------------------
 */
PortalEntry *
pbuf_addEntry()
{
	return (PortalEntry *)
	pbuf_alloc(sizeof(PortalEntry));
}

/* --------------------------------
 *		pbuf_freeEntry - Free a portal entry in the portal table
 *		the portal is freed separately.
 * --------------------------------
 */
void
pbuf_freeEntry(int i)
{
	if (portals)
	{
		pbuf_free((caddr_t) portals[i]);
		portals[i] = NULL;
	}
}


/* --------------------------------
 *		pbuf_freeTypes - Free up the space used by a portal
 * --------------------------------
 */
void
pbuf_freeTypes(TypeBlock *types)
{
	pbuf_free((caddr_t) types);
}

/* --------------------------------
 *		pbuf_freeTuples - free space used by tuple block
 * --------------------------------
 */
void
pbuf_freeTuples(TupleBlock *tuples,
				int no_tuples,
				int no_fields)
{
	int			i,
				j;

	if (no_tuples > TupleBlockSize)
	{
		pbuf_freeTuples(tuples->next, no_tuples - TupleBlockSize, no_fields);
		no_tuples = TupleBlockSize;
	}

	/* For each tuple, free all its attribute values. */
	for (i = 0; i < no_tuples; i++)
	{
		for (j = 0; j < no_fields; j++)
			if (tuples->values[i][j] != NULL)
				pbuf_free((caddr_t) tuples->values[i][j]);
		if (tuples->lengths[i])
			pbuf_free((caddr_t) tuples->lengths[i]);
		if (tuples->values[i])
			pbuf_free((caddr_t) tuples->values[i]);
	}

	pbuf_free((caddr_t) tuples);
}

/* --------------------------------
 *		pbuf_freeGroup - free space used by group, types and tuples
 * --------------------------------
 */
void
pbuf_freeGroup(GroupBuffer *group)
{
	if (group->next != NULL)
		pbuf_freeGroup(group->next);

	if (group->types != NULL)
		pbuf_freeTypes(group->types);

	if (group->tuples != NULL)
		pbuf_freeTuples(group->tuples, group->no_tuples, group->no_fields);

	pbuf_free((caddr_t) group);
}

/* --------------------------------
 *		pbuf_freePortal - free space used by portal and portal's group
 * --------------------------------
 */
void
pbuf_freePortal(PortalBuffer *portal)
{
	if (portal->groups != NULL)
		pbuf_freeGroup(portal->groups);

	pbuf_free((caddr_t) portal);
}

/* --------------------------------
 *		pbuf_getIndex - Return the index of the portal entry
 *		note: portals[] maps portal names to portal buffers.
 * --------------------------------
 */
int
pbuf_getIndex(char *pname)
{
	int			i;

	if (portals)
	{
		for (i = 0; i < (int) portals_array_size; i++)
			if (portals[i] != NULL &&
				strncmp(portals[i]->name, pname, PortalNameLength) == 0)
				return i;
	}

	return -1;
}

/* --------------------------------
 *		pbuf_setportalname - assign a user given name to a portal
 * --------------------------------
 */
void
pbuf_setportalinfo(PortalEntry *entry, char *pname)
{
	if (entry)
		StrNCpy(entry->name, pname, PortalNameLength);
}

/* --------------------------------
 *		pbuf_setup - Set up a portal for dumping data
 * --------------------------------
 */
PortalEntry *
pbuf_setup(char *pname)
{
	int			i;

	if (!portals)				/* the portals array has not been
								 * allocated yet */
	{
		/* allocate portals[] array here */
		portals_realloc(PORTALS_INITIAL_SIZE);
	}

	/* If a portal with the same name already exists, close it. */
	/* else look for an empty entry in the portal table. */
	if ((i = pbuf_getIndex(pname)) != -1)
		pbuf_freePortal(portals[i]->portal);
	else
	{
		for (i = 0; i < (int) portals_array_size; i++)
			if (portals[i] == NULL)
				break;

		/* If the portal table is full, enlarge it */
		if (i >= (int) portals_array_size)
			portals_realloc(PORTALS_GROW_BY);

		portals[i] = pbuf_addEntry();
		strncpy(portals[i]->name, pname, PortalNameLength);
	}
	portals[i]->portal = pbuf_addPortal();
	portals[i]->portalcxt = NULL;
	portals[i]->result = NULL;

	return portals[i];
}

/* --------------------------------
 *		pbuf_close - Close a portal, remove it from the portal table
 *						and free up the space
 * --------------------------------
 */
void
pbuf_close(char *pname)
{
	int			i;

	if ((i = pbuf_getIndex(pname)) == -1)
		libpq_raise(&PortalError, vararg_format("Portal %s does not exist.", pname));

	pbuf_freePortal(portals[i]->portal);
	pbuf_freeEntry(i);
}

/* --------------------------------
 *		pbuf_findGroup - Return the group given the group_index
 * --------------------------------
 */
GroupBuffer *
pbuf_findGroup(PortalBuffer *portal,
			   int group_index)
{
	GroupBuffer *group;

	group = portal->groups;
	while (group_index > 0 && group != NULL)
	{
		group = group->next;
		group_index--;
	}

	if (group == NULL)
		libpq_raise(&PortalError,
			 vararg_format("Group index %d out of bound.", group_index));

	return group;
}

/* --------------------------------
 * pbuf_findFnumber - Return the field index of a given field within a group
 * --------------------------------
 */
int
pbuf_findFnumber(GroupBuffer *group,
				 char *field_name)
{
	TypeBlock  *types;
	int			i;

	types = group->types;

	for (i = 0; i < group->no_fields; i++)
		if (strncmp(types[i].name, field_name, NAMEDATALEN) == 0)
			return i;

	libpq_raise(&PortalError,
			 vararg_format("Field-name %s does not exist.", field_name));

	/* not reached, here to make compiler happy */
	return 0;

}

/* --------------------------------
 *		pbuf_checkFnumber - signal an error if field number is out of bounds
 * --------------------------------
 */
void
pbuf_checkFnumber(GroupBuffer *group,
				  int field_number)
{
	if (field_number < 0 || field_number >= group->no_fields)
		libpq_raise(&PortalError,
		   vararg_format("Field number %d out of bound.", field_number));
}

/* --------------------------------
 *		pbuf_findFname - Find the field name given the field index
 * --------------------------------
 */
char *
pbuf_findFname(GroupBuffer *group,
			   int field_number)
{
	pbuf_checkFnumber(group, field_number);
	return (group->types[field_number]).name;
}
