/*
 * Interface to Postgres from Perl
 *
 * /usr/local/devel/postgres-v4r2/src/contrib/pgperl/RCS/pg-libpq.mus,v 1.7 1994/06/16 11:37:21 aoki Exp
 */

/* 
PMA - Tue Feb 16 14:48:02 PST 1993
Tested on DEC MIPS (Ultrix 4.2) with postgres 4.1 and perl 4 pl 35.

RWW - Tue Feb  2 19:55:17 EST 1993
This is based on the version released with 4.0.1.  I modified it in
the following ways:

  1) I deleted things that were no (obvious) use within perl:  
     PQgroup, PQFlushI, and PQfn.  PQfn might be added again, but
     would require additional support routines to construct the args
     and do something sensible with the result.

  2) I corrected the code so that mus created the right access methods.

  3) I added a missing routine: PQgetlength.

  4) I deleted pg-mus pg-perl.h and changed to just basic mus and perl.h 
     (which was already partly done).

  5) I didn't include any async stuff since that is apparently supposed
     to change soon.

  6) I deleted the version comments, since they didn't compile and were
     probably apocryphal by now.

  7) I made the postgres global variables read-only from perl.  Those
     that are allowed to be set from user code already have routines
     to set them or may be set by using enviroment variables before
     PQsetdb.  The only exception is PQoption, which I allow to be
     set even though it is deprecated.

  8) Fixed definition of PQdatabase to compile correctly.  This may be
     a problem with the SVR4 CCS, but the fix is portable anyway.

  9) Made PQpnames return a perl array.  The sytax of perl doesn't allow
     for non-scalars to be returned as a argument.  Thus the syntax is
     @names = &PQpnames($rule_p)
*/

#include "tmp/libpq.h"
#include "tmp/libpq-fe.h"	/* superset of ones also in libpq-be.h */

#ifdef PORTNAME_ultrix4
#include "config.h"
#undef HAS_MEMCPY
#undef HAS_MEMSET
#endif /* PORTNAME_ultrix4 */

#include "EXTERN.h"
#include "perl.h"

/*
  These typedefs force mus to use the proper access methods.
*/

typedef PortalBuffer PB;

/*
  The LIBPQ globals
*/
extern char *PQhost;
extern char *PQport;
extern char *PQtty;
extern char *PQoption;
extern char PQdatabase[17];	/* From fe-pqexec.c */
extern char PQerrormsg[];
extern int PQportset;
extern int PQxactid;
extern int PQtracep;

static enum uservars {
  UV_PQhost,
  UV_PQport,
  UV_PQtty,
  UV_PQoption,
  UV_PQdatabase,
  UV_PQerrormsg,
  UV_PQportset,
  UV_PQxactid,
  UV_PQtracep
};

static enum usersubs {
  US_PQclear,
  US_PQdb,
  US_PQendcopy,
  US_PQexec, 
  US_PQfinish,
  US_PQfname,
  US_PQfnameGroup,
  US_PQfnumber,
  US_PQfnumberGroup,
  US_PQftype,
  US_PQgetgroup,
  US_PQgetlength,
  US_PQgetline,
  US_PQgetvalue,
  US_PQnfields,
  US_PQnfieldsGroup,
  US_PQngroups,
  US_PQninstances,
  US_PQninstancesGroup,
  US_PQnportals,
  US_PQntuples,
  US_PQntuplesGroup,
  US_PQparray,
  US_PQpnames,
  US_PQputline,
  US_PQreset,
  US_PQrulep,
  US_PQsametype,
  US_PQsetdb,
  US_PQtrace,
  US_PQuntrace,
};

static
unsigned int
dbl2uint(d)
     double d;
{
  unsigned int i = d;
  return i;
}

static
double
uint2dbl(i)
     unsigned int i;
{
  double d = i;
  return d;
}


static int usersub();
static int userset();
static int userval();

extern void
init_postgres_stuff()
{	
  struct ufuncs uf;
  char *filename = "libpq.c";

  uf.uf_set = userset;
  uf.uf_val = userval;

#define MAGICVAR(name, ix) uf.uf_index = ix, magicname(name, &uf, sizeof uf)

  /* register PG variables */
  MAGICVAR("PQhost",		UV_PQhost);
  MAGICVAR("PQport",		UV_PQport);
  MAGICVAR("PQtty",		UV_PQtty);
  MAGICVAR("PQoption",		UV_PQoption);
  MAGICVAR("PQdatabase",	UV_PQdatabase);
  MAGICVAR("PQerrormsg",	UV_PQerrormsg);
  MAGICVAR("PQportset",		UV_PQportset);
  MAGICVAR("PQxactid",		UV_PQxactid);
  MAGICVAR("PQtracep",		UV_PQtracep);

  /* register PG functions */
  make_usub("PQclear",		US_PQclear,		usersub, filename);
  make_usub("PQdb",		US_PQdb,		usersub, filename);
  make_usub("PQendcopy",	US_PQendcopy,		usersub, filename);
  make_usub("PQexec",		US_PQexec,		usersub, filename);
  make_usub("PQfinish",		US_PQfinish,		usersub, filename);
  make_usub("PQfname",		US_PQfname,		usersub, filename);
  make_usub("PQfnameGroup",	US_PQfnameGroup,	usersub, filename);
  make_usub("PQfnumber",	US_PQfnumber,		usersub, filename);
  make_usub("PQfnumberGroup",	US_PQfnumberGroup,	usersub, filename);
  make_usub("PQftype",		US_PQftype,		usersub, filename);
  make_usub("PQgetgroup",	US_PQgetgroup,		usersub, filename);
  make_usub("PQgetlength",	US_PQgetlength,		usersub, filename);
  make_usub("PQgetline",	US_PQgetline,		usersub, filename);
  make_usub("PQgetvalue",	US_PQgetvalue,		usersub, filename);
  make_usub("PQnfields",	US_PQnfields,		usersub, filename);
  make_usub("PQnfieldsGroup",	US_PQnfieldsGroup,	usersub, filename);
  make_usub("PQngroups",	US_PQngroups,		usersub, filename);
  make_usub("PQninstances",	US_PQninstances,	usersub, filename);
  make_usub("PQninstancesGroup", US_PQninstancesGroup,  usersub, filename);
  make_usub("PQnportals",	US_PQnportals,		usersub, filename);
  make_usub("PQntuples",	US_PQntuples,		usersub, filename);
  make_usub("PQntuplesGroup",	US_PQntuplesGroup,	usersub, filename);
  make_usub("PQparray",		US_PQparray,		usersub, filename);
  make_usub("PQpnames",		US_PQpnames,		usersub, filename);
  make_usub("PQputline",	US_PQputline,		usersub, filename);
  make_usub("PQreset",		US_PQreset,		usersub, filename);
  make_usub("PQrulep",		US_PQrulep,		usersub, filename);
  make_usub("PQsametype",	US_PQsametype,		usersub, filename);
  make_usub("PQsetdb",		US_PQsetdb,		usersub, filename);
  make_usub("PQtrace",		US_PQtrace,		usersub, filename);
  make_usub("PQuntrace",	US_PQuntrace,		usersub, filename);
}

static int
usersub(ix, sp, items)
     int ix;
     register int sp;
     register int items;
{
  STR **st = stack->ary_array + sp;
  register int i;
  register char *tmps;
  register STR *Str;		/* used in str_get and str_gnum macros */

  switch (ix) {

/*
 * free storage claimed by named portal.
 */
CASE void PQclear
I char* pname
END

/*
 * Return the current database being accessed.
 */
CASE char* PQdb
END

/*
 * returns 1 if all's well?
 */
CASE int PQendcopy
END

/*
 * Send a query to the POSTGRES backend.
 * The return value is a string.
 * If there is an error: return "E error-message".
 * If tuples are fetched from the backend, return "P portal-name".
 * If a query is executed successfully but no tuples fetched,
 * return "C query-command".
 */
CASE char* PQexec 
I  char* query
END

/*
 * Close communication ports with the backend.
 */
CASE void PQfinish 
END

/*
 * Return the name of a field.
 */
CASE char* PQfname
I PB* portal
I int           tuple_index
I int 		field_number
END

/* Return the field (attribute) name given the group index
 * and field index.
 */
CASE char* PQfnameGroup
I PB* portal
I int           group_index
I int           field_number
END

/*
 * Return the field index of a given field name within a tuple.
 */
CASE int PQfnumber
I PB* portal
I int           tuple_index
I char*         field_name
END

/* Return the field number (index) given the group index and
 * the field name.
 */
CASE int PQfnumberGroup
I  PB* portal
I int            group_index
I char*          field_name
END

/*
 * Return the type of a field.
 */
CASE int PQftype
I PB* portal
I int           tuple_index
I int 		field_number
END

/*
 * Return the index of the group that a particular tuple is in.
 */
CASE int PQgetgroup
I PB* portal
I int           tuple_index
END

/*
  Return an attribute (field) length
*/
CASE int PQgetlength
I PB*    portal
I int    tuple_index
I int    field_number
END

/* 
 * USER FUNCTION - gets a newline-terminated string from the backend.
 */
CASE int PQgetline
IO char* s
I int    maxlen
END

/* 
 * Return an attribute (field) value.
 */
CASE char* PQgetvalue
I PB* portal
I int           tuple_index
I int 		field_number
END

/*
 * Return the number of fields in a tuple.
 */
CASE int PQnfields
I PB* portal
I int           tuple_index
END

/*
 * Return the number of fields in a tuple group.
 */
CASE int PQnfieldsGroup
I  PB* portal
I int            group_index
END

/*
 * Return the number of tuple groups in a portal buffer.
 */
CASE int PQngroups
I  PB* portal
END

/*
 *
 */
CASE int PQninstances
I PB* portal
END

/*
 *
 */
CASE int PQninstancesGroup
I PB* portal
I int           group_index
END

/* 
 * Return the number of open portals. 
 * If rule_p, only return asynchronized portals. 
 */
CASE int PQnportals
I int rule_p
END

/*
 * Return the number of tuples in a portal buffer.
 */
CASE int PQntuples
I  PB* portal
END

/*
 * Return the number of tuples in a tuple group.
 */
CASE int PQntuplesGroup
I  PB* portal
I int            group_index
END

/*
 * Return the portal buffer given a portal name.
 */
CASE PB* PQparray
I char* pname
END

/* 
 * Return all the portal names.
 * If rule_p, only return asynchronized portals. 
 * (The way postgres returns these names is really stupid!)
 */
    case US_PQpnames:
	if (items != 1)
	    fatal("Usage: &PQpnames($rule_p)");
	else {
	    int i, nnames, nportals;
	    char *p, *portalnames, **pnames;
	    int rule_p = (int) str_gnum(st[1]);

	    /* Figure out the maximum number of portals we might have */
	    nportals = PQnportals(rule_p);
	    pnames = (char **) calloc(nportals, sizeof(char *));
	    portalnames = (char *) calloc(nportals, PortalNameLength + 1);

	    /* Fill in the pname array */
	    for (i = 0, p = portalnames; i < nportals;
		 ++i, p += PortalNameLength + 1) {
		pnames[i] = p;
	    }

	    PQpnames(pnames, rule_p);

	    /* Count the number of valid strings */
	    nnames = 0;
	    for (i = 0; i < nportals; ++i) {
		if (pnames[i] && *pnames[i])
		    /* We don't care about the blank portal */
		    ++nnames;
	    }

	    /* Return an array of strings */
	    if (nnames <= 0) {
	      st[0] = str_2mortal(str_make("",0));
	      i = sp;
            } else {
	      astore(stack, sp + nnames, NULL); /* force stack to grow */
	      st = stack->ary_array + sp;  /* stack may have changed */
	      for (i = 1; i <= nnames; i++) {
		  st[i-1] = str_2mortal(str_make( pnames[i-1], 0 ));
	      }
	      i = sp + nnames - 1;
            }
	    free(portalnames);
	    free(pnames);
	    return(i);
	}

/*
 * USER FUNCTION - sends a string to the backend.
 */
CASE int PQputline
I char* s
END

/*
 * Reset the communication port with the backend.
 */
CASE void PQreset 
END

/*
 * Return 1 if an asynchronized portal.
 */
CASE int PQrulep
I  PB* portal
END

/* Return 1 if the two tuples have the same type (in the
 * same group).
 */
CASE int PQsametype
I PB* portal
I int           tuple_index1
I int           tuple_index2
END

/*
 * Make the specified database the current database.
 */
CASE void PQsetdb 
I  char* dbname
END

/*
 * turn on pqdebug() tracing.
 */
CASE void PQtrace 
END

/*
 * turn off pqdebug() tracing.
 */
CASE void PQuntrace 
END

  default:
    fatal("Unimplemented user-defined subroutine");
  }
  return sp;
}

static int
userval(ix, str)
     int ix;
     STR *str;
{
  switch (ix) {
  case UV_PQhost:
    str_set(str, PQhost);
    break;
  case UV_PQport:
    str_set(str, PQport);
    break;
  case UV_PQtty:
    str_set(str, PQtty);
    break;
  case UV_PQoption:
    str_set(str, PQoption);
    break;
  case UV_PQdatabase:
    str_set(str, &PQdatabase[0]);
    break;
  case UV_PQerrormsg:
    str_set(str, &PQerrormsg[0]);
    break;
  case UV_PQportset:
    str_numset(str, (double) PQportset);
    break;
  case UV_PQxactid:
    str_numset(str, (double) PQxactid);
    break;
  case UV_PQtracep:
    str_numset(str, (double) PQtracep);
    break;
  }
  return 0;
}

static int
userset(ix, str)
     int ix;
     STR *str;
{
  static char *optstr = NULL;
  switch (ix) {
  case UV_PQoption:
    if (optstr) free(optstr);
    {
      int len = strlen(str_get(str));
      optstr = malloc(len + 1);
      strcpy(optstr, str_get(str));
    }
    PQoption = optstr;
    break;
  }
  return 0;
}
