/* See the copyright notice (COPYRIGHT) in this directory. */

/*
 * NAME
 *	
 * Public:	pg_open_channel()	- allocate a channel
 *		pg_close_channel() 	- deallocate a channel
 *		pg_channel_is_open()	- is the channel allocated?
 * 
 *		pg_flush()		- flush (deallocate) portal contents
 *		pg_PQpnames()		- replacement for libpq PQpnames
 *		pg_get_attr()		- get a character string from portal
 * Inactive:
 *	This guys currents gets a SEGV. Keep in place once it gets fixed,
 *	but don't call pg_portal_open for now.
 *
 *		pg_portal_open()	- is the POSTGRES portal itself open?
 *
 * Private:	pg_portal_name()	- construct a portal name from channel
 *
 * DESCRIPTION
 *	This file contains functions for managing GDI channels.
 *
 *	Unlike ORACLE cursors and SYBASE dbprocs, POSTGRES portals do not
 *	persist. They function more like Embedded/SQL fetch cursors.
 *
 *	GDI channel management provides persistent data structures within 
 *	which queries can be managed and POSTGRES portals can be used as needed.
 *
 * DIAGNOSTICS
 *	Functions which return pointers return NULL if an error occurs.
 *	Functions which return a non-negative int will return -1 on an error.
 *
 * FILES
 *	pg_channel.c (this file)
 *
 * AUTHOR
 *	Jean T. Anderson, SAIC Open Systems Division
 */

#ifndef	lint
static char SccsId[] = "@(#)pg_channel.c	16.1 8/3/93 Copyright (c) 1992-1993 Science Applications International Corporation";
#endif

#include "gdi_postgres.h"

extern PortalEntry *portals[];

Proto (static char *,   pg_portal_name, (int));

/*
 * pg_open_channel ()
 *
 * Allocate a channel, assign it a default portal name, and attach it to
 * the dbConn.
 *
 * Public
 */

dbStatus
pg_open_channel (conn, channo)
dbConn	*conn;    /* (i) Database Connector */
int	*channo;  /* (o) Channel number assigned by this routine */
{
	char	*routine = "pg_open_channel";
	int	i;
	PRTDEF	**v;
	char	*portal_name;

        if( (conn == NULL) || (conn->vendor_conn == NULL) )
                return( gdi_error_app(conn, GDI_NOCONNECT,
                        "pg_open_channel: not connected to database") );

	if (channo)
		*channo = -1;

	if (channo == NULL)
                return( gdi_error_app(conn, GDI_BADDATA,
                        "pg_open_channel: NULL channo argument") );
	
	/* create channel pointer array, if it has not already been created */

	if (PG_CHANNELS (conn) == NULL)
	{
		if ((PG_CHANNELS (conn) = UCALLOC (PRTDEF *, 1)) == NULL)
			return (gdi_error_unix (conn, routine));
		PG_NUM_CHANNELS (conn) = 1;
	}

	/* Look for a NULL channel pointer to use */

	for	(i = 0; 
		(i < PG_NUM_CHANNELS (conn)) && (PG_CHAN (conn, i) != NULL);
		i++);

	/* Enlarge the channel pointer array if necessary */

	if (i == PG_NUM_CHANNELS (conn))
	{
		if ((v = UREALLOC (PG_CHANNELS (conn), PRTDEF *, PG_NUM_CHANNELS (conn) + 1)) 
			== NULL)
				return (gdi_error_unix (conn, routine));
		PG_CHANNELS (conn) = v;
		PG_NUM_CHANNELS (conn) ++;
	}


	 /* allocate new channel space */

	if ((PG_CHAN (conn, i) = UCALLOC (PRTDEF, 1)) == NULL)
		return (gdi_error_unix (conn, routine));

	/* A Postgres portal is initialized when it is used, so don't try to
	 * "open" it like we do with oracle/sybase.  Instead, assign a code
	 * and portal name to this channel, then record both in the vendor conn.
	 */

	PG_PORT_NUM(conn, i) = i;
	*channo = i;

	if ( (portal_name = pg_portal_name(i)) == (char *)NULL ) 
		return (gdi_error_unix (conn, routine));

	(void) pg_strncpy(PG_PORT_NAME(conn, i), portal_name,
		PG_PORTAL_NAME_SIZE, strlen(portal_name));
	UFREE(portal_name);
	
	if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
		fprintf(stderr, 
		   "%16s: Created channel '%d' (default portal name '%s')\n",
		   routine, *channo, portal_name);

	return (GDI_SUCCESS);
}


/*
 * pg_close_channel ()
 *
 * Deallocate a channel.
 *
 * Public
 */

dbStatus
pg_close_channel (conn, channo)
dbConn	*conn;		/* (i) Database Connector */
int	channo;		/* (o) Channel number assigned by this routine */
{
	char	*routine = "pg_close_channel";
	char	query[24];
	char	*results;
     
        if( (conn == NULL) || (conn->vendor_conn == NULL) )
                return( gdi_error_app(conn, GDI_NOCONNECT,
                        "pg_close_channel: not connected to database"));

	if ( pg_channel_is_open (conn, channo) )
	{
		/* Is the portal associated with this channel open? */
		if(pg_portal_open(conn, channo, PG_ALL_PORTALS))
		{
			sprintf(query, "close %s", PG_PORT_NAME(conn, channo));
			results=PQexec(query);
			if(pg_error(conn,channo,results,routine) != GDI_SUCCESS)
				return(GDI_FAILURE);
		}


		UFREE (PG_CHAN (conn, channo));
		if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
			fprintf(stderr,
				"%16s: Deallocated channel '%d' \n",
				routine,channo);
	}

	return (GDI_SUCCESS);
}

/*
 * pg_channel_is_open ()
 *
 * Determine if a specified GDI channel is allocated.  Return TRUE or FALSE.
 *
 * Public
 */

dbStatus
pg_channel_is_open (conn, channo)
dbConn	*conn;
int	channo;
{
	if ((conn == NULL) || (conn->vendor_conn == NULL) 
	   || (PG_CHANNELS (conn) == NULL))
		return (FALSE);

	if ((channo < 0) || (channo >= PG_NUM_CHANNELS (conn)) 
	   || (PG_CHAN (conn, channo) == NULL))
		return (FALSE);

     return (TRUE);
}


/*
 * pg_flush ()
 *
 * Flush a Postgres channel.
 *
 * Public
 */

dbStatus
pg_flush (conn, channo)
dbConn	*conn;
int	channo;
{
        if( (conn == NULL) || (conn->vendor_conn == NULL) )
                return( gdi_error_app(conn, GDI_NOCONNECT,
                        "pg_flush: not connected to database"));

	if (pg_channel_is_open (conn, channo) != TRUE)
                return( gdi_error_app(conn, GDI_NOCONNECT,
                        "pg_flush: channel not open"));

	if(pg_portal_open(conn, channo, PG_ALL_PORTALS))
		PQclear( PG_PORT_NAME(conn, channo) );

	return (GDI_SUCCESS);
}


/*
 * pg_portal_open ()
 *
 * Is the portal itself already open? Returns TRUE or FALSE.
 * 
 * PQpnames with a 0 rule_p currently gets a SEGV.
 *
 * Public
 */

dbStatus
pg_portal_open(conn, channo, rule_type)
dbConn	*conn;
int	channo;
int	rule_type;	/* PG_ALL_PORTALS (0) or PG_ASYNCH_PORTALS (1) */
{
	char	*routine="pg_portal_open";
	int	n_portals;
	char	pnames[PG_MAX_PORTALS][PortalNameLength];
	int	found_it, i;

        if( (conn == NULL) || (conn->vendor_conn == NULL) )
                return( gdi_error_app(conn, GDI_NOCONNECT,
                        "pg_portal_open: not connected to database"));

	/* PG_ASYNCH_PORTALS (non-0) gets just asynchronous portals.
	 * PG_ALL_PORTALS (0) returns all portals.
	 */
	n_portals=PQnportals(rule_type);

	if (n_portals <= 0)
		return(FALSE);	/* We have no portals too-da-ee-ay */

	if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
		fprintf(stderr,
			"%16s: %d portals are open.\n", routine, n_portals);

	pg_PQpnames(pnames, rule_type);

	for(i=0, found_it=FALSE; i < n_portals && found_it == FALSE; i++)
	{
		if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
			fprintf(stderr,
			"%16s: portal '%s' is open\n", routine, pnames[i]);

		if (!strcmp (pnames[i], PG_PORT_NAME(conn, channo) ) )

			found_it=TRUE;
	}

	if(found_it==FALSE)
		return(FALSE);
	else
		return(TRUE);
}

/*
 * pg_portal_name ()
 *
 * Given a channel number, construct a default Postgres portal name.
 *
 * Private
 */
static
char *
pg_portal_name(channo)
int	channo;
{
	char    portal_name[PG_PORTAL_NAME_SIZE +1];
	char	*result;

		/* Construct portal name from portal number.  */
	sprintf(portal_name, "portal%d", channo);

	if ((result = UCALLOC (char, PG_PORTAL_NAME_SIZE + 1)) == (char *)NULL)
		return ( (char *)NULL );

	(void) pg_strncpy (result, portal_name, 
		PG_PORTAL_NAME_SIZE, strlen(portal_name));

	return(result);
}

/* Slight variation on libpq's PQpnames(), which gets a SEGV on its
 * strcpy for reasons I couldn't figure out.
 */
void
pg_PQpnames(pnames, rule_p)
char	pnames[MAXPORTALS][PortalNameLength];
int	rule_p;
{
    int i;

    for (i = 0; i < MAXPORTALS; i++)
        if (portals[i] != NULL)
        {
            if (!rule_p || portals[i]->portal->rule_p)
                (void) pg_strncpy(pnames[i], portals[i]->name, 
		PortalNameLength - 1, strlen(portals[i]->name));
            else *pnames[i] = NULL;
        }
        else
            *pnames[i] = NULL;
    return;
}
