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

/*
 * NAME
 *
 * Public:
 *	pg_build_dbtype()    -	Get complete database type info given pgType.
 *				Calls pg_builtin_dbtype().
 *				Called by pg_col_defs().
 *	pg_builtin_dbtype_s()-	Find the datatype name of an attribute.
 *	pg_derive_ctype()    -	Get external C type (dbCtype) and its
 *				length given the pgType and coerce option.
 *				Calls pg_derive_default_ctype().
 *				Called by pg_col_defs().
 *	pg_derive_elength()  -	Get external size of types.
 *
 * Private:
 *	pg_check_array()     -	Says if a type is an array type or not.
 *	pg_s2k_types()       -	User-Defined types that support Sequoia 2000.
 *
 *	pg_derive_default_ctype()- Derive a default C type and length.
 *
 * DESCRIPTION
 *	This file contains functions for POSTGRES data type management.
 *	Data type management is a real challenge; we need to revisit this
 *	entire handling on the next cut.
 *
 * FILES
 *	pg_types.c (this file)
 *
 * AUTHOR
 *	J.T. Anderson
 */

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

#include "gdi_postgres.h"

/* ====================  Database Data Type ================================ */

/* pg_build_dbtype ()
 *
 * Build database data type information, including name, array size, and
 * user-defined datatype if it is a known Sequoia 2000 data type.
 *
 * Public
 */

Proto (static void, pg_s2k_types, (pgType *dbtype, char *tname));

Proto (static dbStatus, pg_check_array,
	(dbConn *dbconn, pgType *dbtype, char *dbtype_s, 
	char *col_name, int *array_size));

dbStatus
pg_build_dbtype (dbconn, cname, dbtype, dbtype_s, slen, max_arrlen)
dbConn	*dbconn;	/* (i)	database connector                   */
char	*cname;		/* (i)  column name                          */
pgType	*dbtype;	/* (i)	integer data type                    */
char	*dbtype_s;	/* (o)	database data type name              */
int	slen;		/* (i)	length of dbtype_s                   */
int	*max_arrlen;	/* (o)  number of elements if it is an array */
{
	if (dbtype_s)
		dbtype_s [0] = '\0';
     
	if (dbtype_s == NULL)
	{	(void) gdi_error_app (dbconn, GDI_BADDATA, 
			"pg_build_dbtype_s: NULL pointer argument");
		return (GDI_FAILURE);
	}
	  
	/* What is the data type name? */
	if(pg_builtin_dbtype_s (dbconn, dbtype, dbtype_s) == GDI_FAILURE)
		return(GDI_FAILURE);

	/* Is it an array type?  */
	if( pg_check_array (dbconn, dbtype, dbtype_s, cname, max_arrlen) 
		!= GDI_SUCCESS)
			return (GDI_FAILURE);

	return (GDI_SUCCESS);
}

/* pg_builtin_dbtype_s()
 *
 * Given a POSTGRES data type, get its name.
 *
 * This was private until it turned out to be useful for the s2k data dict
 * dumper.
 */
dbStatus
pg_builtin_dbtype_s(dbconn, dbtype, dbtype_s)
dbConn	*dbconn;
pgType	*dbtype;
char	*dbtype_s;
{
	char	*a;

		/* switch this check to libtable as soon as there is time */
	static char *builtin_types [] =
	{
	"","","","","","","","","","","","","","","","", 	/* 0-15 */
	"bool",		/* 16 */
	"bytea",	/* 17 */
	"char",		/* 18 */
	"char16",	/* 19 */
	"dt",		/* 20 */
	"int2",		/* 21 */
	"int28",	/* 22 */
	"int4",		/* 23 */
	"regproc",	/* 24 */
	"text",		/* 25 */
	"oid",		/* 26 */
	"tid",		/* 27 */
	"xid",		/* 28 */
	"cid",		/* 29 */
	"oid8",		/* 30 */
	"","","","","","","","","","","","","","","","","","","","", /* 31- 50*/
	"","","","","","","","","","","","","","","","","","","","", /*     70*/
	"","","","","","","","","","","","","","","","","","","","", /*     90*/
	"","","","","","","","","","","","","","","","","","","","", /*    110*/
	"","","","","","","","","","","","","","","","","","","","", /*    130*/
	"","","","","","","","","","","","","","","","","","","","", /*    150*/
	"","","","","","","","","","","","","","","","","","","","", /*    170*/
	"","","","","","","","","","","","","","","","","","","","", /*    190*/
	"","","","","","","","","","","","","","","","","","","",    /*191-209*/
	"smgr",		/* 210 */
	"","","","","","","","","","","","","","","","","","","","", /*211-230*/
	"","","","","","","","","","","","","","","","","","","","", /*    250*/
	"","","","","","","","","","","","","","","","","","","","", /*    270*/
	"","","","","","","","","","","","","","","","","","","","", /*    290*/
	"","","","","","","","","","","","","","","","","","","","", /*    310*/
	"","","","","","","","","","","","","","","","","","","","", /*    330*/
	"","","","","","","","","","","","","","","","","","","","", /*    350*/
	"","","","","","","","","","","","","","","","","","","","", /*    370*/
	"","","","","","","","","","","","","","","","","","","","", /*    390*/
	"","","","","","","","","","","","","","","","","","","","", /*    410*/
	"","","","","","","","","","","","","","","","","","","","", /*    430*/
	"","","","","","","","","","","","","","","","","","","","", /*    450*/
	"","","","","","","","","","","","","","","","","","","","", /*    470*/
	"","","","","","","","","","","","","","","","","","","","", /*    490*/
	"","","","","","","","","","","","","","","","","","","","", /*    510*/
	"","","","","","","","","","","","","","","","","","","","", /*    530*/
	"","","","","","","","","","","","","","","","","","","","", /*    550*/
	"","","","","","","","","","","","","","","","","","","","", /*    570*/
	"","","","","","","","","","","","","","","","","","","","", /*    590*/
	"","","","","","","","","",                                  /*591-599*/
	"point", 	/* 600 */
	"lseg", 	/* 601 */
	"path", 	/* 602 */
	"box", 		/* 603 */
	"polygon", 	/* 604 */
	"filename", 	/* 605 */
	"","","","","",                                              /*606-610*/
	"","","","","","","","","","","","","","","","","","","","", /*    630*/
	"","","","","","","","","","","","","","","","","","","","", /*    650*/
	"","","","","","","","","","","","","","","","","","","","", /*    670*/
	"","","","","","","","","","","","","","","","","","","","", /*    690*/
	"","","","","","","","","",                                  /*691-699*/
	"float4", 	/* 700 */
	"float8", 	/* 701 */
	"abstime", 	/* 702 */
	"reltime",	/* 703 */
	"tinterval", 	/* 704 */
	"unknown", 	/* 705 */
	"","","","","",                                              /*706-710*/
	"","","","","","","","","","","","","","","","","","","","", /*    730*/
	"","","","","","","","","","","","","","","","","","","","", /*    750*/
	"","","","","","","","","","","","","","","","","","","","", /*    770*/
	"","","","","","","","","","","","","","","","","","","","", /*    790*/
	"","","","","","","","","","","","","","","","","","","","", /*    810*/
	"","","","","","","","","","","","","","","","","","","","", /*    830*/
	"","","","","","","","","","","","","","","","","","","","", /*    850*/
	"","","","","","","","","","","","","","","","","","","","", /*    870*/
	"","","","","","","","","","","","","","","","","","","","", /*    890*/
	"","","","","","","","","","","","","","","","","","","",    /*891-909*/
	"oidint4",	/* 910 */
	"oidchar16",	/* 911 */
	   "","","","","","","","","","","","","","","","","","","", /*912-930*/
	"","","","","","","","","","","","","","","","","","","","", /*    950*/
	"","","","","","","","","","","","","","","","","","","","", /*    970*/
	"","","","","","","","","","","","","","","","","","","","", /*    990*/
	"","","","","","","","","",                                  /*991-999*/
	"_bool",	/* 1000 */
	"_bytea",	/* 1001 */
	"_char",	/* 1002 */
	"_char16",	/* 1003 */
	"_dt",		/* 1004 */
	"_int2",	/* 1005 */
	"_int28",	/* 1006 */
	"_int4",	/* 1007 */
	"_regproc",	/* 1008 */
	"_text",	/* 1009 */
	"_tid",		/* 1010 */
	"_xid",		/* 1011 */
	"_cid",		/* 1012 */
	"_oid8",	/* 1013 */
	"_lock",	/* 1014 */
	"_stub",	/* 1015 */
	"_ref",		/* 1016 */
	"_point",	/* 1017 */
	"_lseg",	/* 1018 */
	"_path",	/* 1019 */
	"_box",		/* 1020 */
	"_float4",	/* 1021 */
	"_float8",	/* 1022 */
	"_abstime",	/* 1023 */
	"_reltime",	/* 1024 */
	"_tinterval",	/* 1025 */
	"_filename",	/* 1026 */
	"_polygon",	/* 1027 */
	"_oid",		/* 1028 */
	"aclitem",	/* 1033 */
	"_aclitem",	/* 1034 */
	};

	if ((*dbtype <= (sizeof (builtin_types) / sizeof (char *) - 1))
		&& (*dbtype >= 0))
	{	(void) pg_strncpy(dbtype_s, builtin_types [*dbtype], 
			GDI_MAX_TYPE, strlen(builtin_types [*dbtype]));
		return(GDI_SUCCESS);
	}
	else
	{	a=pg_get_type (dbconn, (int) *dbtype);
		if(a == (char *)NULL)
			return(GDI_FAILURE);

		(void) pg_strncpy(dbtype_s, a, GDI_MAX_TYPE, strlen(a));

		UFREE(a);

		/* Is it a Sequoia type? */
		pg_s2k_types(dbtype, dbtype_s);

		return(GDI_SUCCESS);
	}
}

/*
 * pg_s2k_types()
 *
 *	Is the user defined type one of the supported Sequoia 2000 types?
 *	If it is, assign the SK2 type and return TRUE, otherwise
 *	return FALS.
 *
 * Private
 */
static
void
pg_s2k_types(dbtype, tname)
pgType	*dbtype;
char	*tname;
{
	if (STREQL (tname, "char2"))
		*dbtype = PG_CHAR2;
	if (STREQL (tname, "_char2"))
		*dbtype = PG_CHAR2_ARRAY;

	if (STREQL (tname, "char4"))
		*dbtype = PG_CHAR4;
	if (STREQL (tname, "_char4"))
		*dbtype = PG_CHAR4_ARRAY;

	if (STREQL (tname, "char8"))
		*dbtype = PG_CHAR8;
	if (STREQL (tname, "_char8"))
		*dbtype = PG_CHAR8_ARRAY;

	return;
}

static
dbStatus 
pg_check_array (dbconn, dbtype, dbtype_s, col_name, array_size)
dbConn	*dbconn;
pgType	*dbtype;
char	*dbtype_s;
char	*col_name;
int	*array_size;
{
	*array_size = 0;

	if ( (*dbtype == PG_INT28) || (*dbtype == PG_OID8) )
		*array_size = 8;

	if( dbtype_s[0] == '_' )
	{	if ( (pg_get_elem(dbconn, col_name, array_size)) == GDI_FAILURE)
			return (GDI_FAILURE);
		else if (*array_size == -2)
			return (GDI_FAILURE);
	}
	return(GDI_SUCCESS);
}

/* ============================  C Type =================================== */

Proto (static dbStatus, pg_derive_default_ctype,
	(
		dbCoerce  coerce_option,  /* (i) constructor coerce option */
		pgType	  t,		/* (i) internal POSTGRES type	*/
		dbCtype	  *ctype,	/* (o) C type			*/
		int	  *max_strlen	/* (o) C string length		*/
	));

/*
 * pg_derive_ctype ()
 *
 * Derive the C type given the dbtype.
 * Length is only set if the ctype is GDI_STRING.
 *
 * Public
 */
dbStatus
pg_derive_ctype(coerce_option, dbtype, ctype, max_strlen)
dbCoerce	coerce_option;
pgType		dbtype;
dbCtype		*ctype;
int		*max_strlen;
{
	int	ret;
     
	if (ctype)
		*ctype = GDI_UNDEFINED;

	if ((ctype == NULL) || (max_strlen == NULL))
	{	gdi_error_app (NULL, GDI_BADDATA, 
			"pg_derive_ctype: NULL pointer argument");
		return (GDI_FAILURE);
	}

	switch (coerce_option)
	{
		/* Everything to its closest GDI data type match */
	case GDI_DEFAULT_COERCE:
		/* Character data to strings, numbers to doubles. */
	case GDI_DBL_STR_COERCE:
		ret = pg_derive_default_ctype
			(coerce_option, dbtype, ctype, max_strlen);
		break;
	case GDI_DOUBLE_COERCE:		/* Everything to doubles */
		*ctype = GDI_DOUBLE;
		break;
	case GDI_STRING_COERCE:		/* Everything to strings */
		*ctype = GDI_STRING;
		*max_strlen = *max_strlen + 1;
		break;
	default:
		gdi_error_app (NULL, GDI_BADDATA, 
			"pg_derive_ctype: Unknown coerce option");
		return (GDI_FAILURE);
		break;
	}

	return (ret);
}

static
dbStatus
pg_derive_default_ctype(coerce_option, t, ctype, max_strlen)
dbCoerce	coerce_option;
pgType		t;
dbCtype		*ctype;
int		*max_strlen;
{
	/* A note about the coerce options...
	 *
	 * 	GDI_DEFAULT_COERCE:
	 *		Convert everything to its closest GDI data type.
	 * 	GDI_DBL_STR_COERCE:
	 *		Convert character data to strings.
	 *		Convert numbers to doubles.
	 */

	switch(t)
	{
	   case PG_BOOL:		/* c.h */
	   case PG_BOOL_ARRAY:
	   case PG_CHAR:
	   case PG_CHAR_ARRAY:
		if(coerce_option == GDI_DBL_STR_COERCE)
		{	*ctype = GDI_STRING;
			*max_strlen = 2;
		}
		else
			*ctype = GDI_CHAR;
		break;
	   case PG_FILENAME:
	   case PG_FILENAME_ARRAY:
	   case PG_TEXT:
	   case PG_TEXT_ARRAY:
	   case PG_BYTEA:		/* a varlena, which is a text */
	   case PG_BYTEA_ARRAY:
		*ctype = GDI_STRING;
		*max_strlen= *max_strlen+4;
		break;
	   case PG_CHAR2:
	   case PG_CHAR2_ARRAY:
		*ctype = GDI_STRING;
		*max_strlen = (2+1);
		break;
	   case PG_CHAR4:
	   case PG_CHAR4_ARRAY:
		*ctype = GDI_STRING;
		*max_strlen = (4+1);
		break;
	   case PG_CHAR8:
	   case PG_CHAR8_ARRAY:
		*ctype = GDI_STRING;
		*max_strlen = (8+1);
		break;
	   case PG_CHAR16:		/* 16 character array */
	   case PG_CHAR16_ARRAY:
		*ctype = GDI_STRING;
		*max_strlen = (16+1);
		break;
	   case PG_INT2:		/* c.h: signed short    */
	   case PG_INT28:		/* postgres.h: array of 8 int2 */
	   case PG_INT2_ARRAY:
		if(coerce_option == GDI_DBL_STR_COERCE)
			*ctype = GDI_DOUBLE;
		else
			*ctype = GDI_SHORT;
		break;
	   case PG_OID:		/* postgres.h: uint32 (c.h: unsigned long) */	
	   case PG_OID_ARRAY:
	   case PG_OID8:
	   case PG_TID: 
	   case PG_TID_ARRAY: 
	   case PG_XID:
	   case PG_XID_ARRAY:
	   case PG_CID:
	   case PG_CID_ARRAY:
	   case PG_REGPROC:
	   case PG_REGPROC_ARRAY:
		if(coerce_option == GDI_DBL_STR_COERCE)
			*ctype = GDI_DOUBLE;
		else
			*ctype = GDI_ULONG;
		*max_strlen=0;
		break;
	   case PG_INT4:	/* postgres.h: int 32 (c.h: signed long) */
	   case PG_INT4_ARRAY:
	   case PG_DT:
	   case PG_DT_ARRAY:
		if(coerce_option == GDI_DBL_STR_COERCE)
			*ctype = GDI_DOUBLE;
		else
			*ctype = GDI_LONG;	/* postgres.h: signed long */
		*max_strlen=0;
		break;
	   case PG_FLOAT4:
	   case PG_FLOAT4_ARRAY:
		if(coerce_option == GDI_DBL_STR_COERCE)
			*ctype = GDI_DOUBLE;
		else
			*ctype = GDI_FLOAT;	/* postgres.h: float */
		*max_strlen=0;
		break;
	   case PG_FLOAT8:
	   case PG_FLOAT8_ARRAY:
		*ctype = GDI_DOUBLE;	/* postgres.h: double */
		*max_strlen=0;
		break;
	   case PG_ABSTIME:
	   case PG_ABSTIME_ARRAY:
		*ctype = GDI_STRING;
		*max_strlen = PG_DATE_MASK_SIZE;
		break;

		/* I'm not sure what to do with the rest of these yet,
		 * and I'm not sure what to do with the GDI_DBL_STR_COERCE
		 * So coerce all to string for now.
		 */
	   case PG_ACLITEM:
	   case PG_ACLITEM_ARRAY:
	   case PG_BOX:
	   case PG_BOX_ARRAY:
	   case PG_OID8_ARRAY:
	   case PG_INT28_ARRAY:
	   case PG_LSEG:
	   case PG_LSEG_ARRAY:
	   case PG_OIDINT4:
	   case PG_OIDCHAR16:
	   case PG_PATH:
	   case PG_PATH_ARRAY:
	   case PG_POINT:
	   case PG_POINT_ARRAY:
	   case PG_POLYGON:
	   case PG_POLYGON_ARRAY:
	   case PG_RELTIME:
	   case PG_RELTIME_ARRAY:
	   case PG_SMGR:
	   case PG_TINTERVAL:
	   case PG_TINTERVAL_ARRAY:
		*ctype = GDI_STRING;
		break;
	   case PG_UNKNOWN:
		*ctype = GDI_UNDEFINED;
		break;
	   default:
		*ctype = GDI_USER_DEFINED;
		break;
	}
	return(GDI_SUCCESS);
}

dbStatus
pg_derive_etype (ctype, length, elength)
dbCtype ctype;
int     length;
int     *elength;
{

	if (elength == NULL)
	{	(void) gdi_error_app (NULL, GDI_BADDATA, 
			"pg_derive_etype: NULL pointer argument");
		return (GDI_FAILURE);
	}

	switch (ctype)
	{
		case GDI_CHAR:
		case GDI_UCHAR:
			*elength = sizeof (char);
			break;
		case GDI_SHORT:
		case GDI_USHORT:
			*elength = sizeof (short);
			break;
		case GDI_INTEGER:
		case GDI_UINTEGER:
			*elength = sizeof (int);
			break;
		case GDI_LONG:
		case GDI_ULONG:
			*elength = sizeof (long);
			break;
		case GDI_FLOAT:
			*elength = sizeof (float);
			break;
		case GDI_DOUBLE:
			*elength = sizeof (double);
			break;
		case GDI_STRING:
			*elength = length * sizeof (char);
			break;
		default:
			*elength = 0;
			break;
	}
	return (GDI_SUCCESS);
}
