/* Modified PQexec & friends from POSTGRES */

/* 
 * It was necessary to receive the actual error message from the backend
 * In libpq, upon receiving an 'E' + errmsg pair from the backend, 
 * PQexec prints the errmsg on stderr and returns just a single 'R'
 * The message would then end up in a window which started alberi.
 * Also, read_remark has been modified to insert a notice message in
 * textpane1, instead of printing it on the stderr.
 *
 * (KJK)
 */


/* ----------------------------------------------------------------
 *   FILE
 *	fe-pqexec.c
 *	
 *   DESCRIPTION
 *	support for executing POSTGRES commands and functions
 *	from a frontend application.
 *
 *   SUPPORT ROUTINES
 *	read_initstr, read_remark, EstablishComm, process_portal,
 *	StringPointerSet
 *
 *   INTERFACE ROUTINES
 *	PQdb 		- Return the current database being accessed. 
 *	PQsetdb 	- Make the specified database the current database. 
 *	PQreset 	- Reset the communication port with the backend. 
 *	PQfinish 	- Close communication ports with the backend. 
 *	PQFlushI 	- Used for flushing out "poll" queries by the monitor.
 *
 * >>	PQfn 		- Send a function call to the POSTGRES backend.
 * >>	PQexec 		- Send a query to the POSTGRES backend
 *	
 *   NOTES
 *	These routines are NOT compiled into the postgres backend,
 *	rather they end up in libpq.a.
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/lib/libpq/RCS/fe-pqexec.c,v 1.27 1992/08/07 00:38:18 mao Exp $
 * ----------------------------------------------------------------
 */

#include "tmp/c.h"

#include "tmp/simplelists.h"
#include "tmp/libpq-fe.h"
#include "tmp/fastpath.h"

#ifdef POSTGRES_4_1
#include "fmgr.h"
#else
#include "utils/fmgr.h"
#endif
#include "utils/exception.h"

#define DefaultHost	"localhost"
#define DefaultPort	"4321"
#define DefaultTty	"/dev/null"
#define DefaultOption	""
#define DefaultVacuum	"~/bin/vacuumd"

extern char *PQhost;
extern char *PQport;
extern char *PQtty;
extern char *PQoption;
extern char PQdatabase[];
extern int PQportset;
extern int PQxactid;



char *
alberi_PQexec(query)
    char *query;
{
    char id[2];
    char errormsg[error_msg_length];
    char command[command_length];
    static char PQcommand[command_length+1];
    void alberi_EstablishComm(), alberi_read_remark_notice();
    char *alberi_process_portal();
    static char buff[1024];

    /* If the communication is not established, establish it. */
    if (!PQportset)
	alberi_EstablishComm();

    /* Send a query to backend. */
    pq_putnchar("Q", 1);
    pq_putint(PQxactid, 4);
    pq_putstr(query);
    pqdebug("The query sent to the backend: %s", query);
    pq_flush();

    /* forever (or at least until we get something besides a notice) */
    for (;;) {  

    	/* Process return values from the backend. 
	 * The code in this function is the implementation of
	 * the communication protocol.
	 */
    	id[0] = '?';

    	/* Get the identifier. */
    	pq_getnchar(id,0,1); 

    	alberi_read_remark_notice(id);
    	pqdebug("The Identifier is: %c", (char *)id[0]);

    	/* Read in the transaction id. */
    	PQxactid = pq_getint(4);
    	pqdebug("The Transaction Id is: %d", (char *)PQxactid);

    	switch (id[0]) {
    	case 'I':
	    return("I");
	    
    	case 'E':
	    /* An error, return 0. */
	    pq_getstr(errormsg, error_msg_length);
	    pqdebug("%s error encountered.", errormsg);
	      
				/* (KJK) */
	    buff[0] = 'R';	/* set up the answer string "R errmsg" */
	    buff[1] = '\0';
	    strcat( buff, errormsg );
	    return buff;	/* and return it back */
	    
    	case 'N': /* print notice and go back to processing return values */
	    /*
	     * If we get an EOF (i.e. backend quickdies) return an R to the fe
	     */
	    if (pq_getstr(errormsg, error_msg_length) == EOF) {
	      buff[0] = 'R';	/* (KJK) -- same here */
	      buff[1] = '\0';
	      strcat( buff, errormsg );
	      return buff;
	    }
	    pqdebug("%s notice encountered.", errormsg);
	    buff[0] = 'R';
	    buff[1] = '\0';
	    strcat( buff, errormsg );
	    return buff;

    	case 'A': {
	    char relname[16];
	    extern int PQAsyncNotifyWaiting;
	    int pid;
	    PQAsyncNotifyWaiting = 0;
	    
	    /* Asynchronized portal. */
	    /* No tuple transfer at this stage. */
	    pqdebug("%s portal encountered.", "Asynchronized");
	    /* Processed the same way as synchronized portal. */
/*	    return
		process_portal(1);*/
	    pq_getstr(relname,16);
	    pid =pq_getint(4);
	    PQappendNotify(relname,pid);
	}
	    break;
    	case 'P':
	    /* Synchronized (normal) portal. */
	    return
		alberi_process_portal(0);
	    
    	case 'C':
	    /* Query executed successfully. */
	    pq_getstr (command, command_length);
	    pqdebug ("Query command: %s", command);
	    sprintf (PQcommand, "C%s", command);
	    return
		PQcommand;

	case 'B':
	    /* Copy command began successfully - it is sending stuff back...  */
	    return "BCOPY";

	case 'D':
	    /* Copy command began successfully - it is waiting to receive... */
	    return "DCOPY";

    	default:
	    /* The backend violates the protocol. */
	    if (id[0] == '?')
	    	libpq_raise(&ProtocolError, 
			form((int)"No response from the backend, exiting...\n"));
	    else
	    	libpq_raise(&ProtocolError, 
		   form((int)"Unexpected response from the backend, exiting...\n"));
	    exit(1);
    	}
    }
}

void
alberi_EstablishComm()
{
  char *getenv();

    if (!PQportset) { 
	read_initstr();

	if (pq_connect(PQdatabase, getenv("USER"), PQoption, PQhost, PQtty,
			(char *) NULL, (short)atoi(PQport) ) == -1 ) {
	    libpq_raise(&ProtocolError,
	      form((int)"Failed to connect to backend (host=%s, port=%s)",
		   PQhost, PQport));
	}

	pq_flush();
	PQportset = 1;
    }
}

void
alberi_read_remark_notice(id)
    char id[];
{
    char remarks[remark_length];
    char errormsg[error_msg_length];

    while (id[0] == 'R') {
	pq_getstr(remarks, remark_length);
	if (pq_getnchar(id, 0, 1) == EOF) 
	   return;
    }
    while(id[0] == 'N') {
        pq_getstr(errormsg,error_msg_length);
	print_error( &errormsg[0]+4 );
        backend_error( "Postgres backend returned a notice" );
        if (pq_getnchar(id, 0, 1) == EOF)
	   return;
    }
}

/* ----------------
 *	process_portal
 *	
 * 	Process portal queries. 
 * 	Return values are the same as PQexec().
 * ----------------
 */

char *
alberi_process_portal(rule_p)
    int rule_p;
{
    char pname[portal_name_length];
    char id[2];
    char errormsg[error_msg_length];
    char command[command_length];
    char PQcommand[portal_name_length+1];
    static char retbuf[portal_name_length + 1];
    char buff[1024];
 
    /* Read in the portal name. */
    pq_getstr(pname, portal_name_length);
    pqdebug("Portal name = %s", pname);

    /*
     * This for loop is necessary so that NOTICES out of portal processing
     * stuff are handled properly.
     */

    for (;;) {
        /* Read in the identifier following the portal name. */
        pq_getnchar(id, 0, 1);
        read_remark(id);
        pqdebug("Identifier is: %c", (char *)id[0]);

        switch (id[0]) {
        case 'E':
	    /* An error, return 0. */
	    pq_getstr(errormsg, error_msg_length);
	    pqdebug("%s error encountered.", errormsg);
	    /* get past gunk at front of errmsg */

	    buff[0] = 'R';	/* set up the answer string "R errmsg" */
	    buff[1] = '\0';
	    strcat( buff, errormsg+4 );
	    return buff;

        case 'N': /* print notice and go back to processing return values */
	    /*
	     * If we get an EOF (i.e. backend quickdies) return an R to the fe
	     */
	    if (pq_getstr(errormsg, error_msg_length) == EOF) {
	      buff[0] = 'R';	/* (KJK) -- same here */
	      buff[1] = '\0';
	      strcat( buff, errormsg );
	      return buff;
	    }
	    pqdebug("%s notice encountered.", errormsg);
	    buff[0] = 'R';
	    buff[1] = '\0';
	    strcat( buff, errormsg+4 );
	    return buff;

	    break;
	    
        case 'T':
	    /* Tuples are returned, dump data into a portal buffer. */
	    if (dump_data(pname, rule_p) == -1)
	    {
		    return("R");
	    }
	    sprintf(PQcommand, "P%s", pname);
	    strcpy(retbuf, PQcommand);
	    return(retbuf);
	
	    /* Pending data inquiry - return nothing */
        case 'C':
	    /*
	     * Portal query command (e.g., retrieve, close),
	     * no tuple returned.
	     */
	    PQxactid = pq_getint (4);
	    pqdebug("Transaction Id is: %d", (char *)PQxactid);
	    pq_getstr(command, command_length);
	    pqdebug("Query command: %s", command);
	
	    /* Process the portal commands. */
	    if (strcmp(command, "retrieve") == 0) {
	        /* Allocate a portal buffer, if portal table is full, error. */
	        pbuf_setup(pname);
	        return
		    "Cretrieve";
	    } 
	    else if (strcmp (command, "close") == 0) 
	        return
		    "Cclose";
	    else {
	        sprintf(retbuf, "C%s", command);
	        return
		    retbuf;
	    }

        default:
	    {
	        char s[45];

	        PQreset();
	        sprintf(s, "Unexpected identifier in process_portal: %c", id[0]);
	        libpq_raise(&ProtocolError, form ((int)s));
	    }
	}
    }
}
