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

/*
 * NAME
 *
 * Public:
 *	pg_gdi_funcs()	      -	Checks to see if the user-defined functions 
 *				(UDF) used by the gdi-->postgres interface 
 *				exist.  If they don't, this creates them.
 *	pg_get_elem()	     -	Calls udf gdi_attnelems, gets array size.
 *	pg_get_type()        -	Calls udf gdi_get_type, gets type name for oid.
 *	pg_get_relid()       -	Calls udf gdi_get_relid, gets oid for a class.
 *
 * Private:
 *	pg_create_attnelems() -	Creates UDF gdi_attnelems that looks up the
 *				number of elements in an array type.
 *
 *	pg_create_get_type()  -	Creates UDF gdi_get_type that gets a data type
 *				name given its oid.
 *
 *	pg_create_get_relid() -	Creates UDF gdi_get_relid that gets the oid
 *				of a class given its name.
 *
 *	pg_count()	      - Function that does the existence check.
 *	pg_count_cleanup()    - Cleans up pg_count portal
 *
 * DESCRIPTION
 *	These user-defined functions support system catalog lookups, which
 *	could be called frequently. Using UDF's enhances performance since
 *	the functions can be cached. The performance "hit" is on the first
 *	call, successive calls are fast.
 *
 *	Of course, the existence check for the UDF's themselves could also
 *	be a UDF. But we only have two UDF's so far.
 *
 * FILES
 *	pg_udf.c (this file)
 *
 * AUTHOR
 *	J.T. Anderson
 */

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

#include "gdi_postgres.h"
#include "gdi_turbo.h"


Proto (static dbStatus, pg_create_attnelems,	(dbConn *conn));
Proto (static dbStatus, pg_create_get_type,	(dbConn *conn));
Proto (static dbStatus, pg_create_get_relid,	(dbConn *conn));
/* Proto (static int, pg_count, (dbConn *conn, char *query, char *portal)); */
Proto (static void, pg_count_cleanup, (char *portal));


/* ======================  User-Defined Functions ==============================
 * pg_gdi_funcs
 *
 * If the user-defined functions (UDF) the GDI uses have not been created,
 * then create them.
 *
 * Public
 */

dbStatus
pg_gdi_funcs(conn)
dbConn *conn;
{
	char	*routine="pg_gdi_funcs";
	char	DbQuery[100];
	int	count;

	/* ====== Check for gdi_get_type ====== */

	sprintf(DbQuery, 
	   "retrieve portal udf_portal (pg_proc.oid) where pg_proc.proname=\"gdi_get_type\"::char16");

	count=pg_count(conn, DbQuery, "udf_portal");
	if (count < 0)
		return (GDI_FAILURE);

	else if (count == 0) {
		if (pg_create_get_type(conn) == GDI_FAILURE)
			return(GDI_FAILURE);

		if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
		   fprintf(stderr,
			"%16s: Created user-defined function gdi_get_type.\n",
			routine);
	}

	/* ====== Check for gdi_attnelems ====== */

	sprintf(DbQuery, 
	   "retrieve portal udf_portal (pg_proc.oid) where pg_proc.proname=\"gdi_attnelems\"::char16");


        count=pg_count(conn, DbQuery, "udf_portal");
	if (count < 0)
		return (GDI_FAILURE);
	else if (count == 0) {
		if (pg_create_attnelems(conn) == GDI_FAILURE)
			return(GDI_FAILURE);

		if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
		   fprintf(stderr,
			"%16s: Created user-defined function gdi_attnelems.\n", 
			routine);
	}

	/* ====== Check for gdi_get_relid ====== */

	sprintf(DbQuery, 
	   "retrieve portal udf_portal (pg_proc.oid) where pg_proc.proname=\"gdi_get_relid\"::char16");

        count=pg_count(conn, DbQuery, "udf_portal");
	if (count < 0)
		return (GDI_FAILURE);
	else if (count == 0) {
		if (pg_create_get_relid(conn) == GDI_FAILURE)
			return(GDI_FAILURE);

		if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
		   fprintf(stderr,
			"%16s: Created user-defined function gdi_get_relid.\n", 
			routine);
	}
        return(GDI_SUCCESS);
}

/* pg_get_elem ()
 *
 * Given an array type, fill in the number of elements in the array.
 *
 * Possible fill values are:
 *
 *	-2	Couldn't find attribute name.
 *	-1	undimensioned array type
 *	 0	scalar data type
 *	>0	number of elements in a dimensioned array type
 *
 * Returns GDI_SUCCESS or GDI_FAILURE.
 *
 * Public
 */
dbStatus
pg_get_elem(conn, att_name, att_val)
dbConn	*conn;
char    *att_name;
int	*att_val;
{
	char		*routine="pg_get_elem";
	char		buff[100], 
			*res = NULL,
			*a = NULL;
	int		ival, len, status;
	PortalBuffer	*p = NULL;

	*att_val = -2;	/* initialize to bad value */

	sprintf(buff,
		"retrieve (a = gdi_attnelems  (\"%s\"::char16) )",
		att_name);

	if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
                   fprintf(stderr, "%16s: query is '%s'\n", routine, buff);

	res=PQexec(buff);
	if( pg_error(conn, GDI_NOT_USED, res, routine) != GDI_SUCCESS )
		return(-2);

	p = PQparray(++res);
	status = pg_get_attr(p, 0, 0, &a, &len);

	PQclear("blank"); 

	if(status == GDI_UNIXERR) {
		(void) gdi_error_unix(conn, routine);
		return(GDI_FAILURE);
	}
	else if (status == GDI_NODATA)
	{
		sprintf(buff, 
		"%s: Did not find number of elements for array type %s",
			routine, att_name);
		(void) gdi_error_app (conn, GDI_WARNING, buff);
		ival = -2;
	}
	else
		ival = atoi(a);

	UFREE(a);

	*att_val=ival;

	return(GDI_SUCCESS);
}

/* pg_get_type ()
 *
 * Given an oid type, return the name. If an error occurred, return NULL.
 *
 * Public
 */
char *
pg_get_type (conn, oid)
dbConn	*conn;
int	oid;
{
	char		*routine="pg_get_type";
	PortalBuffer	*p = NULL;
	char		*res = NULL,
			*a = NULL;
	char		buff[100];
	int		status, len;

	sprintf(buff,"retrieve(a = gdi_get_type (%d::oid) )", oid);

	res=PQexec(buff);
	if( pg_error(conn, GDI_NOT_USED, res, routine) != GDI_SUCCESS )
		return ( (char *)NULL );

	p = PQparray(++res);
	status = pg_get_attr(p, 0, 0, &a, &len);

	PQclear("blank"); 

	if(status == GDI_UNIXERR)
	{
		(void) gdi_error_unix(conn, routine);
		return( (char *) NULL );
	}
	else if (status == GDI_NODATA)
	{
		sprintf(buff, "%s: Could not find type for oid %d",
			routine, oid);
		(void) gdi_error_app(conn, GDI_NODATA, buff);
		return( (char *) NULL );
	}

	return(a);
}

/* pg_get_relid ()
 *
 * Given the name of a class, this returns its oid.
 *
 * Return values are:
 *
 *	-1	An error occurred.
 *	 >0	The oid
 *
 * Public
 */
int
pg_get_relid(conn, class_name, class_oid)
dbConn	*conn;
char    *class_name;
int	*class_oid;
{
	char		*routine="pg_get_relid";
	char		query[100], 
			*res = NULL, 
			*a = NULL;
	int		ival, status, len;
	PortalBuffer	*p = NULL;

	sprintf(query,
	   "retrieve(answer = gdi_get_relid (\"%s\"::char16) )", class_name);

	res=PQexec(query);
	if( pg_error(conn, GDI_NOT_USED, res, routine) != GDI_SUCCESS )
		return(GDI_FAILURE);

	p = PQparray(++res);
	status = pg_get_attr(p, 0, 0, &a, &len);
	PQclear("blank"); 

	if(status == GDI_UNIXERR) {
		(void) gdi_error_unix(conn, routine);
		return(GDI_FAILURE);
	}
	else if (status == GDI_NODATA)
		ival = -1;
	else
		ival = atoi(a);

	*class_oid = ival;
	UFREE(a);

	return(GDI_SUCCESS);
}

/* pg_create_attnelems()
 *
 * Create the user-defined function gdi_attnelems() if it does not already
 * exist.
 *
 * Private
 */
static
dbStatus
pg_create_attnelems(conn)
dbConn	*conn;
{
	char    	*routine="pg_attnelems";
	char    	DbQuery[300];
	char    	*res;
	dbStatus	status;

	sprintf(DbQuery,
	"define function gdi_attnelems                  \
		(language=\"postquel\", returntype=int4) \
		arg is (char16) as                      \
		\"retrieve (pg_attribute.attnelems)     \
		where pg_attribute.attname = $1\" ");

	res = PQexec(DbQuery);
	status = pg_error(conn, GDI_NOT_USED, res, routine);

	return(status);
}

/* pg_create_get_type()
 *
 * Create the user-defined function gdi_get_type() if it does not already
 * exist.
 */
static
dbStatus
pg_create_get_type(conn)
dbConn	*conn;
{
	char    	*routine="pg_create_get_type";
	char    	DbQuery[300];
	char    	*res;
	dbStatus	status;

	sprintf(DbQuery,
		"define function gdi_get_type                 \
		(language=\"postquel\", returntype=char16)      \
		arg is (oid) as                                 \
		\"retrieve (pg_type.typname) where pg_type.oid = $1\"");

	res = PQexec(DbQuery);
	status = pg_error(conn, GDI_NOT_USED, res, routine);

	return(status);
}

/* pg_create_get_relid()
 *
 * Create the user-defined function gdi_get_relid() if it does not already
 * exist.
 */
static
dbStatus
pg_create_get_relid(conn)
dbConn	*conn;
{
	char		*routine="pg_create_get_relid";
	char		DbQuery[300];
	char		*res;
	dbStatus	status;

	sprintf(DbQuery,
		"define function gdi_get_relid                 \
		(language=\"postquel\", returntype=oid)      \
		arg is (char16) as                                 \
		\"retrieve (pg_class.oid) where pg_class.relname = $1\"");

	res = PQexec(DbQuery);
	status = pg_error(conn, GDI_NOT_USED, res, routine);

	return(status);
}


/* public 
 * Input better be a syntactically correct 'count'.
 * Returns the results of the count.
 */
int
pg_get_count (dbconn, query)
dbConn	*dbconn;
char	*query;
{
        char    *routine="pg_get_count";
        dbObj   *result_obj=NULL;
        int     *count, str_length, arr_length, is_null;
        void    *tuple = NULL;
        int     return_count = -1;

	if( ( query == NULL ) || ( strlen(query) == 0) )
	{       gdi_error_app(dbconn, GDI_BADDATA,
			"pg_get_count: 'query' is a required input parameter.");
		return(-1);
	}

	if(GDI_ERROR_DEBUG(dbconn) == GDI_DEBUG_VERBOSE)
		fprintf(stderr, "%16s: query is '%s'\n", routine, query);

        if(gdi_submit(dbconn, query, GDI_FETCH_ALL, &GDI_TURBO, &result_obj)
                != GDI_SUCCESS)
        {       result_obj = gdi_obj_destroy (result_obj);
                return(-1);
        }

        if ( GDI_OBJ_NUM_TUPLES(result_obj) != 1 )
        {     (void) gdi_error_app (dbconn, GDI_FAILURE,
                   "pg_get_count: query should have returned 1 tuple");
              result_obj = gdi_obj_destroy (result_obj);
              return(-1);
        }

        if ( GDI_OBJ_NUM_COLUMNS(result_obj) != 1 )
        {     (void) gdi_error_app (dbconn, GDI_FAILURE,
                   "pg_get_count: query should have returned 1 attribute");
              result_obj = gdi_obj_destroy (result_obj);
              return(-1);
        }

        if ((tuple = gdi_obj_tuple_retrieve (result_obj, 0)) == NULL)
        {     (void) gdi_error_app (dbconn, GDI_FAILURE,
                   "pg_get_count: could not get tuple");
              result_obj = gdi_obj_destroy (result_obj);
              return(-1);
        }

        if( (count = (int *) gdi_obj_get_data (result_obj, 0, tuple,
               &is_null, &str_length, &arr_length)) == NULL)
        {    fprintf(stderr, "pg_get_count: could not get attribute\n");
             result_obj = gdi_obj_destroy(result_obj);
             return(-1);
        }

        return_count = *count;

        result_obj = gdi_obj_destroy(result_obj);

        return(return_count);
}

/* =========================  pg_count () ================================== 
 * Execute a retrieve, and return the number of tuples fetched.
 *
 * The main purpose is to make sure objects, such as stored procedures and
 * classes, already exist before we try to access them. If they don't exist,
 *
 * 
 */

/* static */
int
pg_count (conn, query, portal)
dbConn	*conn;
char	*query;
char	*portal;
{
        char            *routine="pg_count";
        PortalBuffer    *p;
        char            *res;
        int             ntups;
	char		tmp_query[40];

	if( ( query == NULL ) || ( strlen(query) == 0) )
	{
		gdi_error_app(conn, GDI_BADDATA,
			"pg_count: 'query' is a required input parameter.");
		return(-1);
	}

	if(GDI_ERROR_DEBUG(conn) == GDI_DEBUG_VERBOSE)
		fprintf(stderr, "%16s: query is '%s'\n", routine, query);

	res = (char *) PQexec("begin");
	if( pg_error(conn, GDI_NOT_USED, res, routine) != GDI_SUCCESS) {
		pg_count_cleanup (portal);
		return(-1);
	}

	/* ============ Execute ============ */
	res = (char *) PQexec(query);
	if( pg_error(conn, GDI_NOT_USED, res, routine) != GDI_SUCCESS) {
		pg_count_cleanup (portal);
		return(-1);
	}

	sprintf(tmp_query, "fetch all in %s", portal);

	res = (char *) PQexec(tmp_query);
	if( pg_error(conn, GDI_NOT_USED, res, routine) != GDI_SUCCESS) {
		pg_count_cleanup (portal);
		return(-1);
	}

	if (!res) {
		pg_count_cleanup (portal);
		return(0);
	}

	if (*res != 'P') {
		(void) gdi_error_app(conn, GDI_UNIXERR, "pg_count: no portal!");
		pg_count_cleanup (portal);
		return(GDI_FAILURE);
	}

	p = PQparray(++res);
	ntups = PQntuples(p);

	pg_count_cleanup (portal);
	return(ntups);
}

static
void
pg_count_cleanup (portal)
char	*portal;
{
	char tmp_query[24];

	sprintf(tmp_query, "close %s", portal);
	PQexec (tmp_query);
	PQexec ("end");

	PQclear(portal); 
	return;
}
