/*
 * printtup.c --
 *	Routines to print out tuples to the standard output.
 */

#include <sys/file.h>
#include <stdio.h>
#include <strings.h>

#include "tmp/postgres.h"

RcsId("$Header: /private/postgres/src/access/common/RCS/printtup.c,v 1.7 1992/03/06 22:17:36 clarsen Exp $");

#include "access/heapam.h"
#include "access/htup.h"
#include "access/skey.h"
#include "storage/buf.h"
#include "utils/memutils.h"
#include "utils/fmgr.h"
#include "utils/log.h"

#include "catalog/syscache.h"
#include "catalog/pg_type.h"

/* ----------------
 *	convtypeinfo
 *
 *	Converts an old-style typeinfo (struct attribute array) into
 *	the new-style typeinfo (struct attribute (*) array).
 *
 *	XXX temporary, but still called by the executor.
 * ----------------
 */
struct attribute **
convtypeinfo(natts, att)
    int 		natts;
    struct attribute	*att;
{
    struct	attribute *ap, **rpp, **retatt;
    
    rpp = retatt = (struct attribute **) 
	palloc(natts * sizeof(*rpp) + natts * sizeof(**rpp));
    MemoryCopy(rpp + natts, att, natts * sizeof(**rpp));
    
    ap = (struct attribute *)(rpp + natts);
    while (--natts >= 0)
	*rpp++ = ap++;
    return(retatt);
}

/* ----------------------------------------------------------------
 *	printtup / debugtup support
 * ----------------------------------------------------------------
 */
/* ----------------
 *	typtoout - used by printtup and debugtup
 * ----------------
 */
ObjectId
typtoout(type)
    ObjectId	type;
{
    HeapTuple	typeTuple;

    typeTuple = SearchSysCacheTuple(TYPOID,
				    (char *) type,
				    (char *) NULL,
				    (char *) NULL,
				    (char *) NULL);

    if (HeapTupleIsValid(typeTuple))
	return((ObjectId)
	       ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);

    elog(WARN, "typtoout: Cache lookup of type %d failed", type);
    return(InvalidObjectId);
}

ObjectId
gettypelem(type)
    ObjectId	type;

{
    HeapTuple	typeTuple;

    typeTuple = SearchSysCacheTuple(TYPOID,
				    (char *) type,
				    (char *) NULL,
				    (char *) NULL,
				    (char *) NULL);

    if (HeapTupleIsValid(typeTuple))
	return((ObjectId)
	       ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);

    elog(WARN, "typtoout: Cache lookup of type %d failed", type);
    return(InvalidObjectId);
}

/* ----------------
 *	printtup
 * ----------------
 */
void
printtup(tuple, typeinfo)
    HeapTuple		tuple;
    struct attribute 	*typeinfo[];
{
    int		i, j, k;
    char	*outputstr, *attr;
    Boolean	isnull;
    ObjectId	typoutput;
    
    /* ----------------
     *	tell the frontend to expect new tuple data
     * ----------------
     */
    pq_putnchar("D", 1);
    
    /* ----------------
     *	send a bitmap of which attributes are null
     * ----------------
     */
    j = 0;
    k = 1 << 7;
    for (i = 0; i < tuple->t_natts; ) {
	attr = heap_getattr(tuple, InvalidBuffer, ++i, typeinfo, &isnull);
	if (!isnull)
	    j |= k;
	k >>= 1;
	if (!(i & 7)) {
	    pq_putint(j, 1);
	    j = 0;
	    k = 1 << 7;
	}
    }
    if (i & 7)
	pq_putint(j, 1);
    
    /* ----------------
     *	send the attributes of this tuple
     * ----------------
     */
    for (i = 0; i < tuple->t_natts; ++i) {
	attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
	typoutput = typtoout((ObjectId) typeinfo[i]->atttypid);
	
	if (!isnull && ObjectIdIsValid(typoutput)) {
	    outputstr = fmgr(typoutput, attr, gettypelem(typeinfo[i]->atttypid));
	    pq_putint(strlen(outputstr)+4, 4);
	    pq_putnchar(outputstr, strlen(outputstr));
	    pfree(outputstr);
	}
    }
}

/* ----------------
 *	printatt
 * ----------------
 */
void
printatt(attributeId, attributeP, value)
    unsigned		attributeId;
    struct attribute	*attributeP;
    char			*value;
{
    printf("\t%2d: %s%s%s%s\t(typeid = %lu, len = %d, byval = %c)\n",
	   attributeId,
	   attributeP->attname,
	   value != NULL ? " = \"" : "",
	   value != NULL ? value : "",
	   value != NULL ? "\"" : "",
	   attributeP->atttypid,
	   attributeP->attlen,
	   attributeP->attbyval ? 't' : 'f');
}

/* ----------------
 *	showatts
 * ----------------
 */
showatts(name, natts, attinfo)
    char			*name;
    int			natts;
    struct attribute	*attinfo[];
{
    register int	i;
    
    puts(name);
    for (i = 0; i < natts; ++i)
	printatt((unsigned) i+1, attinfo[i], (char *) NULL);
    printf("\t----\n");
}

/* ----------------
 *	debugtup
 * ----------------
 */
void
debugtup(tuple, typeinfo)
    HeapTuple		tuple;
    struct attribute 	*typeinfo[];
{
    register int	i;
    char		*attr, *value;
    Boolean		isnull;
    ObjectId		typoutput;
    
    for (i = 0; i < tuple->t_natts; ++i) {
	attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
	typoutput = typtoout((ObjectId) typeinfo[i]->atttypid);
	
	if (!isnull && ObjectIdIsValid(typoutput)) {
	    value = fmgr(typoutput, attr, gettypelem(typeinfo[i]->atttypid));
	    printatt((unsigned) i+1, typeinfo[i], value);
	    pfree(value);
	}
    }
    printf("\t----\n");
}


/* ----------------
 *	printtup_internal
 *      Protocol expects either T, D, C, E, or N.
 *      We use a different data prefix, e.g. 'B' instead of 'D' to
 *      indicate a tuple in internal (binary) form.
 *
 *      This is same as printtup, except we don't use the typout func.
 * ----------------
 */
void
printtup_internal(tuple, typeinfo)
    HeapTuple		tuple;
    struct attribute 	*typeinfo[];
{
    int		i, j, k;
    char	*outputstr, *attr;
    Boolean	isnull;
    ObjectId	typoutput;
#if 0
    FILE *f = fopen("/dev/ttyp0","w");
#endif
    
    /* ----------------
     *	tell the frontend to expect new tuple data
     * ----------------
     */
    pq_putnchar("B", 1);
    
    /* ----------------
     *	send a bitmap of which attributes are null
     * ----------------
     */
    j = 0;
    k = 1 << 7;
    for (i = 0; i < tuple->t_natts; ) {
	attr = heap_getattr(tuple, InvalidBuffer, ++i, typeinfo, &isnull);
	if (!isnull)
	  j |= k;
	k >>= 1;
	if (!(i & 7)) {
	    pq_putint(j, 1);
	    j = 0;
	    k = 1 << 7;
	}
    }
    if (i & 7)
      pq_putint(j, 1);
    
    /* ----------------
     *	send the attributes of this tuple
     * ----------------
     */
    for (i = 0; i < tuple->t_natts; ++i) {
	int len;

	attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
	len = typeinfo[i]->attlen;
	if (!isnull) {
	    /* # of bytes, and opaque data */
	    switch (len) {
	      case -1: {
		  /* variable length, assume a varlena structure */
		  int l = VARSIZE(PointerGetDatum(attr));
		  pq_putint(l,4);
		  pq_putnchar(VARDATA(PointerGetDatum(attr)),l);
#if 0
		  {
		  char *d = VARDATA(PointerGetDatum(attr));
		  fprintf(f,"length %d data %x%x%x%x\n",l,*d,*(d+1),*(d+2),*(d+3));
		  }
#endif
	      }   
		break;

	      default:		/* fixed size */
	       if (typeinfo[i]->attbyval) {
		  pq_putint(len,4);
		  pq_putnchar(&attr,len);
#if 0
	          fprintf(f,"byval length %d data %d\n",len,attr);
#endif
	       } else {
		  pq_putint(len,4);
		  pq_putnchar(attr,len);
#if 0
		  fprintf(f,"byref length %d data %x\n",len,attr);
#endif
	       }
	        break;
	    }
	}
    }
#if 0
    fclose(f);
#endif
}
