/*
 * vistest - PostgreSQL visibility and concurrency test utility
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "libpq-fe.h"

/* Prototypes */
static void exit_nicely();

static void check_conn(int whichconn);

static void log_all(const char *text);



/* Global variables */

FILE       **logFile;

PGconn	   **connList;
int			numConns;


static void
exit_nicely()
{
	int i;

	for (i = 0; i < numConns; i++) {
		if (connList[i]) 
			PQfinish(connList[i]);
		if (logFile[i])
			fclose(logFile[i]);
	}
	exit(0);
}

static void
check_conn(int whichconn)
{
	/* check to see that the backend connection was successfully made */
	if (PQstatus(connList[whichconn]) == CONNECTION_BAD)
	{
		fprintf(stderr, "Connection number %d failed .\n", whichconn);
		fprintf(stderr, "%s", PQerrorMessage(connList[whichconn]));
		exit(1);
	}
}

static void
log_all(const char *text)
{
	int i;

	for (i = 0; i < numConns; i++)
	{
		fputs(text,logFile[i]);
	}
	fputs(text,stdout);
}


int
main(int argc, char **argv)
{
	char	   *pghost,
			   *pgport,
			   *pgoptions,
			   *pgtty;
	char	   *dbName;
	char       *fnPrefix;

	char	   *schedName;
	FILE       *schedFile;

	char       queryBuf[1000];
	char       fnameBuf[100];

	PQprintOpt printOpt;
	char       *fsep = " | ";

	int			i;


	PGresult   *res;

	/* Set options */

	pghost = NULL;				/* host name of the backend server */
	pgport = NULL;				/* port of the backend server */
	pgoptions = NULL;			/* special options to start up the backend
								 * server */
	pgtty = NULL;				/* debugging tty for the backend server */

	if (argc != 4)
	{
		fprintf(stderr, "usage: %s dbName sched fnPrefix\n", argv[0]);
		fprintf(stderr, "      runs visibility test schedule sched on dbName \n");
		exit(1);
	}
	dbName = argv[1];
	schedName = argv[2];
	fnPrefix = argv[3];

	schedFile = fopen(schedName,"r");

	if (schedFile == NULL)
	{
		perror("Problem opening schedule file");
		exit(1);
	}

	/* 
	 * The file must start with a START command, followed by the 
	 * number of backends to use 
	 */

	if (fgets(queryBuf,sizeof(queryBuf),schedFile) == NULL)
	{
		fputs("Schedule file is empty\n",stderr);
		exit(1);
	}

	if (! strncasecmp(queryBuf,"START",5))
	{
		numConns = strtol(&queryBuf[5],NULL,10);
		if (numConns < 1)
		{
			fputs("Must start at least one backend\n",stderr);
			exit(1);
		}

		connList = malloc(numConns * sizeof(PGconn *));
		if (connList == NULL)
		{
			fputs("Could not allocate memory for connection list\n",stderr);
			exit(1);
		}

		logFile = malloc(numConns * sizeof(FILE *));
		if (logFile == NULL)
		{
			fputs("Could not allocate memory for logfiles\n",stderr);
			exit(1);
		}

		for (i = 0; i < numConns; i++)
		{
			connList[i] = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
			check_conn(i);
			snprintf(fnameBuf, sizeof(fnameBuf),"%s_%d",fnPrefix,i);
			logFile[i] = fopen(fnameBuf,"w");
			if (logFile[i] == NULL)
			{
				perror ("Failed to open logFile");
				exit(1);
			}
			
		}
	}
	else
	{
		fputs("Schedule contains no START line\n",stderr);
		exit(1);
	}

	/* Set options for PQprintOpt */
	printOpt.header = (pqbool) 1;
	printOpt.align = (pqbool) 1;
    printOpt.standard = (pqbool) 0;
    printOpt.html3 = (pqbool) 0;
    printOpt.expanded = (pqbool) 0;
    printOpt.pager = (pqbool) 0;
    printOpt.fieldSep = fsep;
    printOpt.tableOpt = NULL;
    printOpt.caption = NULL;
    printOpt.fieldName = NULL;

	/* Now the main loop begins */

	while (fgets(queryBuf,sizeof(queryBuf),schedFile)) 
	{
		/* Check for the different options */
		if (! strncasecmp(queryBuf,"--",2))
		{
			log_all(queryBuf);
		} 
		else if (! strncasecmp(queryBuf,"STEP",4))
		{
			/* End of step, so sleep for processing then test for result */
			sleep(2);
			for (i = 0; i < numConns; i++)
			{
				if (!PQconsumeInput(connList[i]))
				{
					fprintf(stderr,"ConsumeInput Error: %s",
							PQerrorMessage(connList[i]));
				}
				else
				{
					if (!PQisBusy(connList[i]))
					{
						while ((res = PQgetResult(connList[i])) != NULL)
						{
							ExecStatusType pqrs = PQresultStatus(res);
							if (pqrs == PGRES_TUPLES_OK)
							{
								PQprint(logFile[i],res,&printOpt);
							}
							else if (pqrs != PGRES_COMMAND_OK)
							{
								fprintf(logFile[i],"%s: %s\n",
										PQresStatus(pqrs), 
										PQresultErrorMessage(res));
							}
							
						}
					}
				}
			}
			log_all(queryBuf);
		}
		else if (queryBuf[0] == 'Q')
		{
			int bnum;
			char *eptr = queryBuf;
			
			bnum = strtol(&queryBuf[1],&eptr,10);
			if ((bnum < 0) || (bnum >= numConns))
			{
				fprintf(stderr,"Query uses invalid connection(%d): %s\n",
						bnum, eptr);
			}
			else
			{

				fprintf(stdout,"Conn %d: %s\n",bnum,eptr);

				if (!PQsendQuery(connList[bnum],eptr))
				{
					fprintf(stderr,"Query (to %d) failed: %s\n",
							bnum,eptr);
					fprintf(stderr,"Message: %s\n",
							PQerrorMessage(connList[bnum]));
				}
			}
		}
		else if (!strncasecmp(queryBuf,"WAIT",4))
		{
			int slp;
			slp = strtol(&queryBuf[5], NULL,10);
			sleep(slp);
		}
		/* Ignore anything unrecognised */

	}
	exit_nicely();
    return 0;

}
			


