/* ----------------------------------------------------------------
 *   FILE
 *	fe-dumpdata.c
 *	
 *   DESCRIPTION
 *	Dump the returned tuples into a frontend buffer
 *
 *   INTERFACE ROUTINES
 *	dump_type 	- Dump the attributes
 *	dump_tuple 	- Dump a tuple
 *	finish_dump 	- End of a command (data stream)
 *	dump_data 	- Read and process the data stream from backend
 *
 *   NOTES
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/lib/libpq/RCS/fe-dumpdata.c,v 1.11 1992/07/15 05:11:02 mao Exp $
 * ----------------------------------------------------------------
 */

#include "tmp/c.h"

#include "tmp/simplelists.h"
#include "tmp/libpq-fe.h"
#include "utils/exc.h"

RcsId("$Header: /private/postgres/src/lib/libpq/RCS/fe-dumpdata.c,v 1.11 1992/07/15 05:11:02 mao Exp $");

/* Define constants. */

#define BYTELEN 8
#define MAXBYTES 128	/* MAXFIELDS / BYTELEN */
#define MAXFIELDS 512

/* --------------------------------
 *	dump_type - Dump the attributes
 * --------------------------------
 */
void
dump_type(types, nfields)
    TypeBlock *types;
    int nfields;
{
    int i;
    TypeBlock *type;

    type = types;

    for (i = 0; i < nfields; i++) {
	pq_getstr(type->name, NameLength);
	type->adtid = pq_getint(4);
	type->adtsize = pq_getint(2);
	type++;
    }
}

/* --------------------------------
 *	dump_tuple - Dump a tuple
 * --------------------------------
 */
void
dump_tuple(values,lengths, nfields)
    char **values;
     int *lengths;
    int nfields;
{
    char 	bitmap[MAXFIELDS];
    int 	bitmap_index = 0;
    int 	i;
    unsigned 	nbytes;		/* the number of bytes in bitmap */
    char 	bmap;		/* One byte of the bitmap */
    int 	bitcnt = 0; 	/* number of bits examined in current byte */
    int 	vlen;		/* length of the current field value */
    
    nbytes = nfields / BYTELEN;
    if ((nfields % BYTELEN) > 0) 
	nbytes++;

    pq_getnchar(bitmap, 0, nbytes);
    bmap = bitmap[bitmap_index];
    
    /* Read in all the attribute values. */
    for (i = 0; i < nfields; i++) {
	/* If the field value is absent, do nothing. */
	if (!(bmap & 0200))
	    values[i] = NULL;
	else {
	    /* Get the value length (the first four bytes are for length). */
	    vlen = pq_getint(4) - 4;
	    /* Allocate storage for the value. */
	    values[i] = pbuf_addValues(vlen + 1);
	    /* Read in the value. */
	    pq_getnchar(values[i], 0, vlen);
	    lengths[i] = vlen;
	    /* Put an end of string there to make life easier. */
	    values[i][vlen] = '\0';
	    pqdebug("%s", values[i]);
	}

	/* Get the approriate bitmap. */
	bitcnt++;
	if (bitcnt == BYTELEN) {
	    bitmap_index++;
	    bmap = bitmap[bitmap_index];
	    bitcnt = 0;
	} else 
	    bmap <<= 1;
    }
}
/* --------------------------------
 *	dump_tuple_internal - Dump a tuple in internal format
 * --------------------------------
 */
void
dump_tuple_internal(values, lengths, nfields)
    char **values;
     int *lengths;
    int nfields;
{
    char 	bitmap[MAXFIELDS];
    int 	bitmap_index = 0;
    int 	i;
    unsigned 	nbytes;		/* the number of bytes in bitmap */
    char 	bmap;		/* One byte of the bitmap */
    int 	bitcnt = 0; 	/* number of bits examined in current byte */
    int 	vlen;		/* length of the current field value */
    
    nbytes = nfields / BYTELEN;
    if ((nfields % BYTELEN) > 0) 
	nbytes++;

    pq_getnchar(bitmap, 0, nbytes);
    bmap = bitmap[bitmap_index];
    
    /* Read in all the attribute values. */
    for (i = 0; i < nfields; i++) {
	/* If the field value is absent, do nothing. */
	if (!(bmap & 0200))
	    values[i] = NULL;
	else {
	    /* For each attribute, we get:
	       Length (4 bytes),
	       Data (n bytes)
	       */
	    
	    vlen = pq_getint(4);
	    /* Allocate storage for the value. */
	    values[i] = pbuf_addValues(vlen + 1);
	    /* Read in the value. */
	    pq_getnchar(values[i], 0, vlen);
	    lengths[i] = vlen;
	    /* Put an end of string there to make life easier. */
	    values[i][vlen] = '\0';
	    pqdebug("%s", values[i]);
	}

	/* Get the approriate bitmap. */
	bitcnt++;
	if (bitcnt == BYTELEN) {
	    bitmap_index++;
	    bmap = bitmap[bitmap_index];
	    bitcnt = 0;
	} else 
	    bmap <<= 1;
    }
}

/* --------------------------------
 *	finish_dump - End of a command (data stream)
 * --------------------------------
 */
void
finish_dump()
{
    char command[command_length];
    int temp;
    
    temp = pq_getint(4);
    pq_getstr(command, command_length);
    
    pqdebug("return code is %d",(char *)temp);
    pqdebug("command is %s",command);
}

/* --------------------------------
 *	dump_data - Read and process the data stream from backend
 * --------------------------------
 */
int 
dump_data(portal_name, rule_p)
    char *portal_name;
    int rule_p;
{
    char 	 id[2];
    char 	 pname[portal_name_length];
    PortalEntry  *entry = NULL;
    PortalBuffer *portal = NULL;
    GroupBuffer  *group = NULL;
    TypeBlock 	 *types = NULL;
    TupleBlock 	 *tuples = NULL;

    int ntuples = 0;	/* the number of tuples in current group */
    int nfields = 0;	/* the number of fields in current group */

    strcpy(pname, portal_name);

    /* If portal buffer is not allocated, do it now. */
    /* if ((portal = PQparray(pname)) == NULL) */
    entry = pbuf_setup(pname);
    portal = entry->portal;
    
    /* If an asynchronized portal, set the flag. */
    if (rule_p)
	portal->rule_p = 1;

    /* Dump_data is called only when id[0] = 'T'. */
    id[0] = 'T';
    
    /* Process the data stream. */
    while (1) {
	switch (id[0]) {
	case 'T':
	    /* A new tuple group. */
	    
	    /* If this is not the first group, record the number of 
	       tuples in the previous group. */
	    if (group != NULL) {
		group->no_tuples = ntuples;
		/* Add the number of tuples in last group to the total. */
		portal->no_tuples += ntuples;
	    }
	    
	    /* Increment the number of tuple groups. */
	    portal->no_groups++;
	    group = pbuf_addGroup(portal);
	    
	    /* Read in the number of fields (attributes) for this group. */
	    nfields = group->no_fields = pq_getint(2);
	    if (nfields > 0) {
	        types = group->types = pbuf_addTypes(nfields);
	        dump_type(types, nfields);
	    }
	    break;

	case 'B':
	    /* A tuple in internal (binary) format. */
	    
	    /* If no tuple block yet, allocate one. */
	    /* If the current block is full, allocate another one. */
	    if (group->tuples == NULL) {
		tuples = group->tuples = pbuf_addTuples();
		tuples->tuple_index = 0;
	    } else if (tuples->tuple_index == TupleBlockSize) {
		tuples->next = pbuf_addTuples();
		tuples = tuples->next;
		tuples->tuple_index = 0;
	    }
		
	    /* Allocate space for a tuple. */
	    tuples->values[tuples->tuple_index] =pbuf_addTuple(nfields);
	    tuples->lengths[tuples->tuple_index] = pbuf_addTupleValueLengths(nfields);

	    /* Dump a tuple internal format. */
	    dump_tuple_internal(tuples->values[tuples->tuple_index],
				tuples->lengths[tuples->tuple_index],
				nfields);
	    ntuples++;
	    tuples->tuple_index++;
	    break;

	case 'D':
	    /* A tuple. */
	    
	    /* If no tuple block yet, allocate one. */
	    /* If the current block is full, allocate another one. */
	    if (group->tuples == NULL) {
		tuples = group->tuples = pbuf_addTuples();
		tuples->tuple_index = 0;
	    } else if (tuples->tuple_index == TupleBlockSize) {
		tuples->next = pbuf_addTuples();
		tuples = tuples->next;
		tuples->tuple_index = 0;
	    }
		
	    /* Allocate space for a tuple. */
	    tuples->values[tuples->tuple_index] =pbuf_addTuple(nfields);
	    tuples->lengths[tuples->tuple_index] = pbuf_addTupleValueLengths(nfields);
	    
	    /* Dump a tuple. */
	    dump_tuple(tuples->values[tuples->tuple_index],
		       tuples->lengths[tuples->tuple_index],
		       nfields);
	    ntuples++;
	    tuples->tuple_index++;
	    break;
#if 0
	case 'A':
	    /* Tuples returned by alerters. */
	    /* Finish up with the current portal. */
	    group->no_tuples = ntuples;
	    portal->no_tuples += ntuples;

	    /* Process the asynchronized portal. */
	    /* This part of the protocol is not very clear. */
	    pq_getint(4);
	    pq_getstr(pname, portal_name_length);
	    pqdebug("Asynchronized portal: %s", pname);
	    
	    entry = pbuf_setup(pname);
	    portal = entry->portal;
	    portal->rule_p = 1;
	    group = NULL;
	    ntuples = 0;
	    nfields = 0;
	    tuples = NULL;
	    break;
#endif

	case 'C':
	    /* Command, end of the data stream. */
	    /* Record the number of tuples in the last group. */
	    group->no_tuples = ntuples;
	    portal->no_tuples += ntuples;
	    finish_dump();
	    return(1);
	    
	case 'E':
		/* YES - THIS CAN HAPPEN - for instance when dynamic loading fails!!! */

	    pq_getstr(PQerrormsg, error_msg_length);
	    pqdebug("%s error encountered.", PQerrormsg);

		/*
		 * use PQerrormsg[4] because there is garbage in the first four bytes..
		 */
	    bcopy(&PQerrormsg[4],PQerrormsg,strlen(PQerrormsg)-4);
	    fprintf(stderr,"%s\n", PQerrormsg);
	    fflush(stderr);
		return(-1);
	case 'N':
	    pq_getstr(PQerrormsg, error_msg_length);
	    pqdebug("%s error encountered.", PQerrormsg);

		/*
		 * use errormsg[4] because there is garbage in the first four bytes..
		 */
	    bcopy(&PQerrormsg[4],PQerrormsg,strlen(PQerrormsg)-4);
	    fprintf(stderr,"%s\n", PQerrormsg);
	    fflush(stderr);
		break;
	default:
	    {
		char s[40];

		/* This should never happen. */
		sprintf(s, "Unexpected identfier in dump_data: %c", id[0]);
		libpq_raise(&ProtocolError, form((int)s));
	    }
	}
	
    	pq_getnchar(id,0,1); 
    	read_remark(id);
	pqdebug("The identifier is: %c", (char *)id[0]);
    }
}
