/*-------------------------------------------------------------------------
 *
 * pqutils.c--
 *    a bunch of routines to make dealing with libpq a little easier
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /usr/local/devel/pglite/cvs/src/bin/psql/pqutils.c,v 1.4 1995/05/01 20:38:44 andrew Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
#include <string.h>
#include <varargs.h>

#include "libpq-fe.h"

extern char PQerrormsg[];

#include "pqutils.h"

int g_pqutil_debug = 0;

#define PQUTILS_ERRLOG_MAXLEN 512

/*
 * text2str -
 *    takes a text varlena and returns a char* 
 *    the CALLER is responsible for freeing the memory of the char* returned
 */
char *
text2str(text* textarg)
{
    int len;
    char* result;

    if (textarg == NULL)	/* NULL text* */
	return "";
    len = VARSIZE(textarg) - VARHDRSZ;
    result = (char*)palloc(len+1); 
    strncpy(result, VARDATA(textarg), len);
    result[len] = '\0';
    return result;
}

/*
 * str2text -
 *    takes a null-terminated characters string and convert it to a text
 *    varlena
 */
text *
str2text(char* str)
{
    int size;
    text* result;

    if (str == NULL)
	{
	    size = VARHDRSZ + 1;
	    str = "";
	}
    else
	size = strlen(str) + 1 + VARHDRSZ;
  
    result = (text*)palloc(size);
    VARSIZE(result) = size;
    strcpy(VARDATA(result), str);
    return result;
}


/*
 *  pq_exec -
 *     like PQexec
 *     send a query to the postgres backend 
 *     checks the libpq result, reporting an error if there is one 
 * 
 *     returns the result string
 * 
 *     the first argument is the query string,  the second is the name of the
 *     procedure sending the query (used for more meaningful error messages)
 */
char *
pq_exec(char* querybuf, char* where)
{
    char* res;

    if (g_pqutil_debug & PQUTIL_DEBUG_EXEC)
	fprintf(stderr,"sending query: %s: %s\n", where, querybuf);

    res = PQexec(querybuf);
    if ( (*res == 'E') || (*res == 'R') )
	{
	    /*
	       pqutils_err(PQUTILS_ERROR, "%s: error while executing query: \n\t %s\n", where, querybuf);
	       pqutils_err(PQUTILS_ERROR, "result = %s, error is %s\n", res, PQerrormsg);
	       */
	    /* an error message will be given higher up in the libtdb layer */
	    return "E";
	}

    if (g_pqutil_debug & PQUTIL_DEBUG_EXEC)
	fprintf(stderr,"query completed, res = %s\n",res);

    return res;
}

/*
 * do_query -
 *    like pq_exec
 *    returns a PortalBuffer if the query result is a portal,
 *    otherwise, return NULL.
 */
PortalBuffer *
do_query(char* querybuf, char* where)
{
    char* res;
    res = pq_exec(querybuf,where);
    if (*res != 'P')
	{
	    pqutils_err(PQUTILS_ERROR, "error: no portal from \n%s\n",querybuf);
	    return (PortalBuffer*)NULL;
	}
    else
	return PQparray(++res);
}     

/*
 * get_attr -
 *    get an attribute from the buffer, by tuple number and attribute number
 *    The attribute value must be a null terminated string.  
 *    (in other words, use this only for ascii portals, not binary portals)
 *    the CALLER is responsible for freeing the string returned
 *    NULL attributes are converted to the NULLATTR_STRING
 */
char *
get_attr(PortalBuffer *portalbuf, int tupno, int attno)
{
    char *attval;
    char *result;

    if (g_pqutil_debug & PQUTIL_DEBUG_GETATTR)
	fprintf(stderr,"calling get_attr() with portalbuf = %X, tupno = %d, attno = %d\n",
		portalbuf, tupno, attno);

    attval = PQgetvalue(portalbuf, tupno, attno);
    if (attval)
	{
	    result = (char *) palloc(strlen(attval) + 1);
	    strcpy(result, attval);
	}
    else
	{
	    result = (char *) palloc(strlen(NULLATTR_STRING) + 1);
	    strcpy(result, NULLATTR_STRING);
	}
    if (g_pqutil_debug & PQUTIL_DEBUG_GETATTR)
	fprintf(stderr,"exiting get_attr()\n");
    return (result);
}

/*
 * get_attr_long -
 *    use this only if the attribute is a long
 *    this saves the effort of allocating and freeing space for the string 
 *    if the attribute is null, 0 is returned.
 */
long
get_attr_long(PortalBuffer *portalbuf, int tupno, int attno)
{
    char *attval;
    long result = 0;

    attval = PQgetvalue(portalbuf, tupno, attno);
    if (attval)
	result = strtol(attval,NULL,10);
    return result;
}

/*
 * bin_get_attr -
 *    get an attribute from a binary portal.  the result is a void*
 *    the CALLER is responsible for freeing the space allocated
 */
void *
bin_get_attr(PortalBuffer *portalbuf, int tupno, int attno)
{
    struct varlena *attval;
    void *result;
    int size;
    
    attval = (struct varlena*)((char*)PQgetvalue(portalbuf, tupno, attno) - VARHDRSZ);

    if (attval)
	{
	    size = PQgetlength(portalbuf, tupno, attno);
	    attval->vl_len = size + VARHDRSZ;
	    result = (void *) palloc(size);
	    memmove(result,attval,size);
	}
    else
	{
	    result = (void *) palloc(strlen(NULLATTR_STRING) + 1);
	    strcpy(result, NULLATTR_STRING);
	}

    return (result);
}

/*
 * bin_get_attr_char16 -
 *    use this for getting char16 attributes from binary portals.
 *    returns a null-terminated string.
 *    CALLER is responsible for freeing the space allocated.
 */
char *
bin_get_attr_char16(PortalBuffer *portalbuf, int tupno, int attno)
{
    char* attval;
    char* result;
    int size;
    
    attval = (char*)PQgetvalue(portalbuf, tupno, attno);

    if (attval)
	{
	    size = PQgetlength(portalbuf, tupno, attno);
	    /* size-4 because the size field is included, +1 for the '\0' */
	    size -= VARHDRSZ;
	    result = (char *) palloc(size+1);
	    memmove(result, attval, size);
	    result[size] = '\0';
	}
    else
	{
	    result = (char *) palloc(strlen(NULLATTR_STRING) + 1);
	    strcpy(result, NULLATTR_STRING);
	}
    return (result);
}

/*
 * bin_get_attr_text -
 *    use this for getting text attributes from binary portals.
 *    returns a null-terminated string.
 *    CALLER is responsible for freeing the space allocated.
 */
char *
bin_get_attr_text(PortalBuffer *portalbuf, int tupno, int attno)
{
    text* attval;
    char* result;
    int size;
    
    attval = (struct varlena*)((char*)PQgetvalue(portalbuf, tupno, attno) - VARHDRSZ);
    attval->vl_len  = PQgetlength(portalbuf, tupno, attno) + VARHDRSZ;

    if (attval)
	result = text2str(attval);
    else
	{
	    result = (char *) palloc(strlen(NULLATTR_STRING) + 1);
	    strcpy(result, NULLATTR_STRING);
	}
    return (result);
}

/*
 * bin_get_attr_long -
 *    use this for getting attributes of type long from binary portals.
 *    returns a long
 *    if the attribute is null, 0 is returned
 */
long
bin_get_attr_long(PortalBuffer* portalbuf, int tupno, int attno) 
{
    long *attval;
    long result = 0;

    attval = (long*)PQgetvalue(portalbuf, tupno, attno);
    if (attval)
	result = *attval;
    return result;
}


/*
 * parseTextArray -
 *  
 *    parse an text[]:
 *    given a string and an integer length, parse into an array of char*,
 *    input strings are of the form   {"A","B","C","D"} 
 *    
 *    the CALLER is responsible for allocating sufficient number of char*'s
 *     in the string array,
 *
 *     parseTextArray will allocate space for each individual char*
 */
void
parseTextArray(char* str, int num, char** strArray)
{
  int i;
  char* beginQuote;
  char* endQuote;
  int nextlen;

  if (*str != '{')
      {
	  pqutils_err(PQUTILS_WARN,"parseTextArray(): badly formed string, must have { as first character\n");
	  return;
      }
  str++; /* skip the first { */
  for (i=0;i<num;i++)
      {
	  if (*str == '\0')
	      { pqutils_err(PQUTILS_WARN,"parseTextArray(): text string ended prematurely\n");
		return;
	    }
	  if  ( (beginQuote = strchr(str,'"')) == NULL)
	      {
		  pqutils_err(PQUTILS_WARN,"parseTextArray():  missing a begin quote\n");
		  return;
	      }
	  if  ( (endQuote = strchr(beginQuote+1,'"')) == NULL)
	      {
		  pqutils_err(PQUTILS_WARN,"parseTextArray():  missing a begin quote\n");
		  return;
	      }
	  nextlen = endQuote - beginQuote; /* don't subtract one here because we need the extra character for \0 anyway */
	  strArray[i] =(char*) malloc(nextlen);
	  strncpy(strArray[i], beginQuote+1, nextlen-1);
	  strArray[i][nextlen-1] ='\0';
	  str = endQuote + 1;
      }
}

/*
 * a varargs interface to print out pqutils errors,  similar to elog
 */
void 
pqutils_err(va_alist)
     va_dcl
{
    va_list ap;
    register int    lev;
    char            *fmt;
    char            buf[PQUTILS_ERRLOG_MAXLEN], line[PQUTILS_ERRLOG_MAXLEN];
    register char   *bp, *cp;
    extern  int     errno, sys_nerr;
    extern  char    *sys_errlist[], *ctime();
    time_t          tim, time();
    int             i = 0;

    va_start(ap);
    lev = va_arg(ap, int);
    fmt = va_arg(ap, char *);
    switch (lev) {
    case PQUTILS_DEBUG:
	cp = "DEBUG:";
	break;
    case PQUTILS_WARN:
	cp = "WARN:";
	break;
    case PQUTILS_ERROR:
	cp = "ERROR:";
	break;
    default:
	sprintf(line, "FATAL %d:", lev);
	cp = line;
    }
    time(&tim);
    strcat(strcpy(buf, cp), ctime(&tim)+4);
    bp = buf+strlen(buf)-6;
    *bp++ = ':';
    while (i-- >0) *bp++ = ' ';
    for (cp = fmt; *cp; cp++)
	if (*cp == '%' && *(cp+1) == 'm') {
	    if (errno < sys_nerr && errno >= 0)
		strcpy(bp, sys_errlist[errno]);
	    else
		sprintf(bp, "error %d", errno);
	    bp += strlen(bp);
	    cp++;
	} else
	    *bp++ = *cp;
    *bp = '\0';
    vsprintf(line, buf, ap);
    va_end(ap);
    strcat(line, "\n");
    fputs(line, stderr);
}

/*
 * this is a stub for the real GetAttributeByName.  This helps us in
 * being to compile and link certain modules both statically and dynamically
 */
char *
GetAttributeByName(TUPLE tuple, char *attname, bool *isnull)
{
    pqutils_err(PQUTILS_ERROR, "GetAttributeByName should never be called by statically linked procedure!");
    exit(1);
    return NULL;
}
