
/*
 * 
 * POSTGRES Data Base Management System
 * 
 * Copyright (c) 1988 Regents of the University of California
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for educational, research, and non-profit purposes and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of the University of California not be used in advertising
 * or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Permission to incorporate this
 * software into commercial products can be obtained from the Campus
 * Software Office, 295 Evans Hall, University of California, Berkeley,
 * Ca., 94720 provided only that the the requestor give the University
 * of California a free licence to any derived software for educational
 * and research purposes.  The University of California makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 * 
 */



/* 
 * dumpdata.c --
 *	Dump the returned tuples into a frontend buffer.
 */

#include "c.h"

#include "libpq.h"
#include "exception.h"

RcsId ("$Header: dumpdata.c,v 1.1 89/01/17 05:55:15 cimarron Exp $");

/* Define constansts. */
#define BYTELEN 8
#define MAXBYTES 128	/* MAXFIELDS / BYTELEN */
#define MAXFIELDS 512

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

    type = types;

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


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

    getpchar (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 = getpint (4) - 4;
	    /* Allocate storage for the value. */
	    values[i] = addValues (vlen + 1);
	    /* Read in the value. */
	    getpchar (values[i], 0, 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];
	}
	else 
	    bmap <<= 1;
    }
}

/* End of a command (data stream). */
void
finish_dump ()
{
	char command[command_length];

	getpint (4);
	getpstr (command, command_length);
}

/* 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];
    PortalBuffer *portal;
    GroupBuffer *group = NULL;
    TypeBlock *types;
    TupleBlock *tuples;

    int ntuples = 0;	/* the number of tuples in current group */
    int nfields = 0;	/* the number of fields in current group */
    int tuple_index = 0;	/* index of next available entry in current 
			   tuple block */

    strcpy (pname, portal_name);

    /* If portal buffer is not allocated, do it now. */
    if ((portal = PQparray (pname)) == NULL)
	portal = portal_setup (pname);
    
    /* 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 = addGroup (portal);
	    
	    /* Read in the number of fields (attributes) for this group. */
	    nfields = group->no_fields = getpint (2);
	    types = group->types = addTypes (nfields);
	    dump_type (types, nfields);
	    break;

	case 'D':
	    /* A tuple. */
	    
	    /* If no tuple block yet, allocate one. */
	    if (group->tuples == NULL) {
		tuples = group->tuples = addTuples ();
		tuple_index = 0;
	    } 
	    /* If the current block is full, allocate another one. */
	    else if (tuple_index == TupleBlockSize) {
		tuples->next = addTuples ();
		tuples = tuples->next;
		tuple_index = 0;
	    }
		
	    /* Allocate space for a tuple. */
	    tuples->values[tuple_index] = addTuple (nfields);
	    /* Dump a tuple. */
	    dump_tuple (tuples->values[tuple_index], nfields);
	    ntuples++;
	    tuple_index++;
	    break;

	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. */
	    getpint (4);
	    getpstr (pname, portal_name_length);
	    pqdebug ("Asynchronized portal: %s", pname);
	    portal = portal_setup (pname);
	    portal->rule_p = 1;
	    group = NULL;
	    ntuples = 0;
	    nfields = 0;
	    tuple_index = 0;
	    break;

	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);

	default:
	    /* This should never happen. */
	    libpq_raise(ProtocolError, form("Unexpected Identifier."));
	}
	
	if (getpchar (id, 0, 1) == -1) 
	    libpq_raise(ProtocolError, form("Communication terminated by backend."));
	pqdebug ("The identifier is: %c", id[0]);
    }
}
