
/* Module:          qresult.c
 *
 * Description:     This module contains functions related to 
 *                  managing result information (i.e, fetching rows from the backend,
 *                  managing the tuple cache, etc.) and retrieving it.
 *                  Depending on the situation, a QResultClass will hold either data
 *                  from the backend or a manually built result (see "qresult.h" to
 *                  see which functions/macros are for manual or backend results.
 *                  For manually built results, the QResultClass simply points to 
 *                  TupleList and ColumnInfo structures, which actually hold the data.
 *
 * Classes:         QResultClass (Functions prefix: "QR_")
 *
 * API functions:   none
 *
 * Comments:        See "notice.txt" for copyright and license information.
 *
 */

#include "qresult.h"
#include "misc.h"
#include <stdio.h>

extern GLOBAL_VALUES globals;

/*  Used for building a Manual Result only */
/*	All info functions call this function to create the manual result set. */
void 
QR_set_num_fields(QResultClass *self, int new_num_fields)
{
	mylog("in QR_set_num_fields\n");

    CI_set_num_fields(self->fields, new_num_fields);
    if(self->manual_tuples) 
        TL_Destructor(self->manual_tuples);

    self->manual_tuples = TL_Constructor(new_num_fields);

	mylog("exit QR_set_num_fields\n");
}

/************************************/
/* CLASS QResult                    */
/************************************/

QResultClass *
QR_Constructor()
{
QResultClass *rv;

	mylog("in QR_Constructor\n");
	rv = (QResultClass *) malloc(sizeof(QResultClass));

	if (rv != NULL) {
		rv->status = PGRES_EMPTY_QUERY;

		/* construct the column info */
		if ( ! (rv->fields = CI_Constructor())) {
			free(rv);
			return NULL;
		}
		rv->manual_tuples = NULL;	
		rv->backend_tuples = NULL;	
		rv->message = NULL;
		rv->command = NULL;
		rv->notice = NULL;
		rv->conn = NULL;
		rv->inTuples = FALSE;
		rv->fcount = 0;
		rv->fetch_count = 0;
		rv->num_fields = 0;
		rv->tupleField = NULL;
		rv->cursor = NULL;
	}

	mylog("exit QR_Constructor\n");
	return rv;
}

void
QR_Destructor(QResultClass *self)
{
	mylog("QResult: in DESTRUCTOR\n");

	/* manual result set tuples */
	if (self->manual_tuples)
		TL_Destructor(self->manual_tuples);

	//	If conn is defined, then we may have used "backend_tuples",
	//	so in case we need to, free it up.  Also, close the cursor.
	if (self->conn && self->conn->sock && CC_is_in_trans(self->conn))
		QR_close(self);			// close the cursor if there is one

	QR_free_memory(self);	// safe to call anyway

	//	Should have been freed in the close() but just in case...
	if (self->cursor)
		free(self->cursor);

	/*	Free up column info */
	if (self->fields)
		CI_Destructor(self->fields);

	/*	Free command info (this is from strdup()) */
	if (self->command)
		free(self->command);

	/*	Free notice info (this is from strdup()) */
	if (self->notice)
		free(self->notice);

	free(self);

	mylog("QResult: exit DESTRUCTOR\n");

}

void
QR_set_command(QResultClass *self, char *msg)
{
	if (self->command)
		free(self->command);

	self->command = msg ? strdup(msg) : NULL;
}

void 
QR_set_notice(QResultClass *self, char *msg)
{
	if (self->notice)
		free(self->notice);

	self->notice = msg ? strdup(msg) : NULL;
}

void 
QR_free_memory(QResultClass *self)
{
register int lf, row;
register TupleField *tuple = self->backend_tuples;
int fcount = self->fcount;
int num_fields = self->num_fields;

	mylog("QResult: free memory in, fcount=%d\n", fcount);

	if ( self->backend_tuples) {

		for (row = 0; row < fcount; row++) {
			mylog("row = %d, num_fields = %d\n", row, num_fields);
			for (lf=0; lf < num_fields; lf++) {
				if (tuple[lf].value != NULL) {
					mylog("free [lf=%d] %u\n", lf, tuple[lf].value);
					free(tuple[lf].value);
				}
			}
			tuple += num_fields;  // next row
		}

		free(self->backend_tuples);
		self->backend_tuples = NULL;
	}

	self->fcount = 0;

	mylog("QResult: free memory out\n");
}

//	This function is called by send_query()
char
QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
{
	//	If called from send_query the first time (conn != NULL), 
	//	then set the inTuples state,
	//	and read the tuples.  If conn is NULL,
	//	it implies that we are being called from next_tuple(),
	//	like to get more rows so don't call next_tuple again!
	if (conn != NULL) {
		self->conn = conn;

		mylog("QR_fetch_tuples: cursor = '%s', self->cursor=%u\n", cursor, self->cursor);

		if (self->cursor)
			free(self->cursor);

		if ( ! cursor || cursor[0] == '\0') {
			self->status = PGRES_INTERNAL_ERROR;
			QR_set_message(self, "Internal Error -- no cursor for fetch");
			return FALSE;
		}
		self->cursor = strdup(cursor);

		//	Read the field attributes.
		//	$$$$ Should do some error control HERE! $$$$
		if ( CI_read_fields(self->fields, CC_get_socket(self->conn))) {
			self->status = PGRES_FIELDS_OK;
			self->num_fields = CI_get_num_fields(self->fields);
		}
		else {
			self->status = PGRES_BAD_RESPONSE;
			QR_set_message(self, "Error reading field information");
			return FALSE;
		}

		mylog("QR_fetch_tuples: past CI_read_fields: num_fields = %d\n", self->num_fields);

		/* allocate memory for the tuple cache */
		self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * globals.fetch_max);
		if ( ! self->backend_tuples) {
			self->status = PGRES_FATAL_ERROR; 
			QR_set_message(self, "Could not get memory for tuple cache.");
			return FALSE;
		}

		self->inTuples = TRUE;

		/*  Force a read to occur in next_tuple */
		self->fcount = globals.fetch_max+1;
		self->fetch_count = globals.fetch_max+1;

		return QR_next_tuple(self);
	}
	else {
		//	Always have to read the field attributes.
		//	But we dont have to reallocate memory for them!

		if ( ! CI_read_fields(NULL, CC_get_socket(self->conn))) {
			self->status = PGRES_BAD_RESPONSE;
			QR_set_message(self, "Error reading field information");
			return FALSE;
		}
	}
}

//	Close the cursor and end the transaction
//	We only close cursor/end the transaction if a cursor was used.
int
QR_close(QResultClass *self)
{
QResultClass *res;

	if (self->conn && self->cursor) {
		char buf[64];

		sprintf(buf, "close %s; END", self->cursor);

		mylog("QResult: closing cursor: '%s'\n", buf);

		res = CC_send_query(self->conn, buf, NULL, NULL);
		CC_set_no_trans(self->conn);

		self->inTuples = FALSE;
		free(self->cursor);
		self->cursor = NULL;

		if (res == NULL) {
			self->status = PGRES_FATAL_ERROR;
			QR_set_message(self, "Error closing cursor.");
			return FALSE;
		}

	}

	return TRUE;
}

//	This function is called by fetch_tuples() AND SQLFetch()
int
QR_next_tuple(QResultClass *self)
{
int id;
QResultClass *res;
SocketClass *sock;
/* Speed up access */
int fetch_count = self->fetch_count;
int fcount = self->fcount;
TupleField *the_tuples = self->backend_tuples;
static char msgbuffer[MAX_MESSAGE_LEN+1];
char cmdbuffer[MAX_MESSAGE_LEN+1];	// QR_set_command() dups this string so dont need static

	if (fetch_count < fcount) {	/* return a row from cache */
		mylog("next_tuple: fetch_count < fcount: returning tuple %d, fcount = %d\n", fetch_count, fcount);
		self->tupleField = the_tuples + (fetch_count * self->num_fields); /* next row */
		self->fetch_count++;
		return TRUE;
	}
	else if (self->fcount < globals.fetch_max) {   /* last row from cache */
			//	We are done because we didn't even get FETCH_MAX tuples
		  mylog("next_tuple: fcount < FETCH_MAX: fcount = %d, fetch_count = %d\n", fcount, fetch_count);
		  self->tupleField = NULL;
		  self->status = PGRES_END_TUPLES;
		  return -1;	/* end of tuples */
	}
	else {	
		/*	See if we need to fetch another group of rows.
			We may be being called from send_query(), and
			if so, don't send another fetch, just fall through
			and read the tuples.
		*/
		self->tupleField = NULL;
		if ( ! self->inTuples) {
			char fetch[128];

			sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor);

			mylog("next_tuple: sending actual fetch (%d) query '%s'\n", globals.fetch_max, fetch);

			//	don't read ahead for the next tuple (self) !
			res = CC_send_query(self->conn, fetch, self, NULL);
			if (res == NULL) {
				self->status = PGRES_FATAL_ERROR;
				QR_set_message(self, "Error fetching next group.");
				return FALSE;
			}
			self->inTuples = TRUE;
			/* This is a true fetch, like SQLFetch() */
			self->fetch_count = 1;		
		}
		else {
			mylog("next_tuple: inTuples = true, falling through: fcount = %d, fetch_count = %d\n", self->fcount, self->fetch_count);
			/*	This is a pre-fetch (fetching rows right after query
				but before any real SQLFetch() calls.  This is done
				so the field attributes are available.
			*/
			self->fetch_count = 0;
		}
		// fall through and read the next group
	}


	self->fcount = 0;
	sock = CC_get_socket(self->conn);
	self->tupleField = NULL;

	for ( ; ;) {

		id = SOCK_get_char(sock);
		switch (id) {
			case 'T': /* Tuples within tuples cannot be handled */
			  self->status = PGRES_BAD_RESPONSE;
			  QR_set_message(self, "Tuples within tuples cannot be handled");
			  return FALSE;
			case 'B': /* Tuples in binary format */
			case 'D': /* Tuples in ASCII format  */
			  if ( ! QR_read_tuple(self, (char) (id == 0))) {
				 self->status = PGRES_BAD_RESPONSE;
				 QR_set_message(self, "Error reading the tuple");
				 return FALSE;
			  }

			  self->fcount++;
			  break;	// continue reading


			case 'C': /* End of tuple list */
			  SOCK_get_string(sock, cmdbuffer, MAX_MESSAGE_LEN);
			  QR_set_command(self, cmdbuffer);

			  mylog("end of tuple list -- setting inUse to false: this = %u\n", self);

				self->inTuples = FALSE;
				if (self->fcount > 0) {

					qlog("    [ fetched %d rows ]\n", self->fcount);
					mylog("_next_tuple: 'C' fetch_max && fcount = %d\n", self->fcount);

					/*  set to first row */
					self->tupleField = the_tuples;
					return TRUE;
				} 
				else { //	We are surely done here (we read 0 tuples)
					qlog("    [ fetched 0 rows ]\n");
					mylog("_next_tuple: 'C': DONE (fcount == 0)\n");
					return -1;	/* end of tuples */
				}

			case 'E': /* Error */
			  SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
			  QR_set_message(self, msgbuffer);
			  self->status = PGRES_FATAL_ERROR;
 			  CC_set_no_trans(self->conn);
				qlog("ERROR from backend in next_tuple: '%s'\n", msgbuffer);

			  return FALSE;

			case 'N': /* Notice */
			  SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH);
			  QR_set_message(self, msgbuffer);
			  self->status = PGRES_NONFATAL_ERROR;
				qlog("NOTICE from backend in next_tuple: '%s'\n", msgbuffer);
			  continue;

			default: /* this should only happen if the backend dumped core */
			  QR_set_message(self, "Unexpected result from backend. It probably crashed");
			  self->status = PGRES_FATAL_ERROR;
 			  CC_set_no_trans(self->conn);
			  return FALSE;
		}
	}
	return TRUE;
}

char
QR_read_tuple(QResultClass *self, char binary)
{
Int2 field_lf;
TupleField *this_tuplefield;
char bmp, bitmap[MAX_FIELDS];        /* Max. len of the bitmap */
Int2 bitmaplen;                       /* len of the bitmap in bytes */
Int2 bitmap_pos;
Int2 bitcnt;
Int4 len;
char *buffer;
int num_fields = self->num_fields;	// speed up access
SocketClass *sock = CC_get_socket(self->conn);


	/* set the current row to read the fields into */
	this_tuplefield = self->backend_tuples + (self->fcount * num_fields);

	bitmaplen = (Int2) num_fields / BYTELEN;
	if ((num_fields % BYTELEN) > 0)
		bitmaplen++;

	/*
		At first the server sends a bitmap that indicates which
		database fields are null
	*/
	SOCK_get_n_char(sock, bitmap, bitmaplen);

	bitmap_pos = 0;
	bitcnt = 0;
	bmp = bitmap[bitmap_pos];

	for(field_lf = 0; field_lf < num_fields; field_lf++) {
		/* Check if the current field is NULL */
		if(!(bmp & 0200)) {
			/* YES, it is NULL ! */
			this_tuplefield[field_lf].len = 0;
			this_tuplefield[field_lf].value = 0;
		} else {
			/*
			NO, the field is not null. so get at first the
			length of the field (four bytes)
			*/
			len = SOCK_get_int(sock, VARHDRSZ);
			if (!binary)
				len -= VARHDRSZ;

			buffer = (char *)malloc(len+1);
			SOCK_get_n_char(sock, buffer, len);
			buffer[len] = '\0';

			// mylog("qresult: len=%d, buffer='%s'\n", len, buffer);

			this_tuplefield[field_lf].len = len;
			this_tuplefield[field_lf].value = buffer;
		}
		/*
		Now adjust for the next bit to be scanned in the
		next loop.
		*/
		bitcnt++;
		if (BYTELEN == bitcnt) {
			bitmap_pos++;
			bmp = bitmap[bitmap_pos];
			bitcnt = 0;
		} else
			bmp <<= 1;
	}
	return TRUE;
}
