#.TH SqlPipe Onyx
#.SH NAME
#	SqlPipe.ppg \- Base-Grammar for SQL's over Pipes

include "unistd.h"
include "memory.h"
include "fcntl.h"
include "stdlib.h"
include "sys/wait.h"
include "sys/stat.h"
include "signal.h"
include "errno.h"

class SqlPipe : SqlParser {
	int sql_in,sql_out,sql_err;
	long sql_timeout;

	SqlPipe();
	virtual ~SqlPipe();

	virtual int statement();
	virtual int select();
	virtual int other();

	virtual int sql_pipe(int *in,int *out,int *err);
	virtual int sql_close();

	virtual void sql_readpipe(int fd,int min,int timeout,Text *res);
	virtual void sql_read();

	virtual int sql_send(char *line);
	virtual int sql_sendmatch();
	virtual int sql_up();
	virtual void sql_purge();
	}

#.SH DESCRIPTION
#	SqlPipe is a Base\-Grammar for SQL's over Pipes. Childs of this
#	class are SqlInformix and SqlShql, which only overloads sql_open().
#
#	Care has to be taken for the sql_timeout variable to make this
#	fast enough be dont lose results of the Engine.

statement : {	sql_timeout = 0;
		}

	database | select | other
	;
#
#	Here database, select and other statements are checkt.

select : {	char line[256];
		}

	"select" matchexp {
		if (sql_up()) {
			sprintf(line,"unload to /tmp/sql_%4.4x select ",
				getpid());
			sql_send(line);
			sql_sendmatch();
			}
		/*
		fprintf(stderr,"sql_in = %d ",sql_in);
		fprintf(stderr,"sql_out = %d ",sql_out);
		fprintf(stderr,"sql_err = %d ",sql_err);
		*/
		}
	;
#
#	This makes a unload statement form a select statement, so
#	it's posible to use the result. Informix has the stupid way
#	to somtimes use format the result like a table, and sometimes
#	like a sheet. Stupid comercial software.

other : {	if (p_val) free(p_val);
		p_val = 0;

		sql_timeout=1;
		}
	true

	[ "insert" | "update" | "delete" ]

	{	if (p_val && sql_up()) {
			sql_send(p_val);
			sql_send(" ");
			sql_timeout=0;
			}
		}

	matchexp {
		if (sql_up()) {
			sql_sendmatch();
			}
		}
	.
#
#	All other statements are directly piped to the engine. If
#	It's insert, update or delete sql_timeout is set to 0 so
#	always wait for a result use them be calling sql_num().
#	Because I dont know if other statements have result, there
#	will be no wait for them.

#.SH AUTHOR
#       Michael Koehne <kraehe@bakunin.north.de>

{ /* ----------------------------------------------------------------------- */

#ifdef _SunOS
extern "C" {
	extern int usleep();
	}
#endif

SqlPipe::SqlPipe() : SqlParser()
{	sql_in = 0;
	sql_out = 0;
	sql_err = 0;
	sql_timeout = 0;
	sql_row_type = 1;
	}

SqlPipe::~SqlPipe() { }

/* ------------------------------------------------------------------------- */

int SqlPipe::sql_pipe(int *in,int *out,int *err)
{	return(makepipe(in,out,err));
	}

static void sql_signal(int sig)
{	signal(SIGPIPE,sql_signal);
	signal(SIGALRM,SIG_IGN);
        }


void SqlPipe::sql_readpipe(int fd,int min,int timeout,Text *res)
{	int lf, savestat;
	char buff[513],line[256],*p,*q;
	int i,count;

	if (fd<=0) return;
	if (!sql_up()) return;

	p=line; memset(line,0,256); count=0;

	/*
	fprintf(stderr,"# sql_readpipe %d %d %d\n",fd,min,timeout);
	*/

	if (min) {
		/*
		fprintf(stderr,"sql_readpipe waiting\n");
		*/
		signal(SIGPIPE,sql_signal);
		signal(SIGALRM,sql_signal);
		alarm(30);
		lf = read(fd,line,min);
		signal(SIGALRM,SIG_IGN);

		if (lf > 0)
			p += lf;
		else {	char err[128];
			sql_close();
			message("error:connection to sql lost by readmin");
			if (errno==4)
				message("error:sql timeout");
			else {	sprintf(err,"error:sql_readpipe errno = %d\n",errno);
				message(err);
				}
			return;
			}
		}

	savestat = fcntl(fd,F_GETFL);
	fcntl(fd, F_SETFL, savestat | O_NDELAY);
	
	/*
	fprintf(stderr,"sql_readpipe timout = %o\n",savestat);
	*/

	do {	memset(buff,0,513);
		lf = read(fd, buff, 512) ;
		if ((lf <0) && (errno == EAGAIN)) lf=0;
		if ((lf <0) && (errno == EACCES)) lf=0;
#ifdef EWOULDBLOCK
		if ((lf <0) && (errno == EWOULDBLOCK)) lf=0;
#endif
		/*
		fprintf(stderr,"sql_readpipe read %d,%d\r",count,lf);
		*/

		if ((!lf) && (timeout>0)) {
			i = timeout;
			while (i--) {
				/*
				fprintf(stderr,"sql_readpipe check %d,%d\r",i,lf);
				*/
				usleep(100);
				memset(buff,0,513);
				lf = read(fd, buff, 1) ;
				if ((lf <0) && (errno == EAGAIN)) lf=0;
				if ((lf <0) && (errno == EACCES)) lf=0;
#ifdef EWOULDBLOCK
				if ((lf <0) && (errno == EWOULDBLOCK)) lf=0;
#endif
				if (lf>0) break;
				}
			}

		if (lf>0) {
			count+=lf;

			q=buff;
			while (*q) {
				*p=*q; q++;

				if (*p == '\n') {
					*p = 0;
					if (p != line)
						if (res) res->add_line(line);
					p=line; memset(line,0,256);
					}
				else 
				if (*p == '\r')
					*p=0;
				else	p++;
				}

			if (p-line > 254) {
				*p = 0;
				if (p != line) if (res) res->add_line(line);
				p=line; memset(line,0,256);
				}
			}

		if (lf < 0) {
			message("error:connection to sql lost by read %d",
				(char*)errno);
			/*
			sql_close();
			return;
			*/
			}
		} while (lf>0);

	fcntl(fd, F_SETFL, savestat & ~O_NDELAY) ;

	return;
	}

void SqlPipe::sql_purge()
{
					/* First throw away old errors */

	if (sql_err>0) sql_readpipe(sql_err,0,0,0);

					/* Now throw away old stdout */

	if (sql_err>0) sql_readpipe(sql_out,0,0,0);

					/* Now do a normal purge */

	SqlParser::sql_purge();
	}

int SqlPipe::sql_send(char *line)
{	
	if (!sql_up()) return(0);

	signal(SIGPIPE,sql_signal);
	if (write(sql_in,line,strlen(line))<=0) {
		sql_close();
		message("error:connection to sql lost by write");
		}

	return(1);
	}

void SqlPipe::sql_read()
{	char line[256];

	if (!sql_up()) return;

	if (sql_timeout >=0) 
		sql_readpipe(sql_err, !sql_timeout,
			sql_timeout ? sql_timeout : 10,sql_results);

	sprintf(line,"/tmp/sql_%4.4x",getpid());
	sql_row_fd = open(line,O_RDONLY);
	if (sql_row_fd) unlink(line);

	return;
	}

int SqlPipe::sql_sendmatch()
{	char **p;
	int i;

	if (!sql_up()) return(0);

	i = 0;
	
	if (p=p_matched->lines) {
		while (*p) {
			sql_send(*p);
			i += strlen(*p);
			p++;
			if (*p) {
				if (i+strlen(*p)<80)
					sql_send(" ");
				else {	sql_send("\n");
					i=0;
					}
				}
			}
		sql_send(";\n\n");
		}
	return(0);
	}

int SqlPipe::sql_close()
{	int status=0;

	/*
	fprintf(stderr,"pipe_close\n");
	*/

	if (sql_database) {
		free(sql_database);
		sql_database=0;

		close(sql_out); sql_out=0;
		close(sql_err); sql_err=0;
		close(sql_in);  sql_in=0;

		wait(&status);
		}

	sql_purge();

	return(status);
	}

int SqlPipe::sql_up()
{	if (!sql_database) {
		/* message("SQL is down. No database defined."); */
		return(0);
		}
	if (sql_in<=0) {
		/* message("SQL is down. sql_in = %d.",(char*)sql_in); */
		return(0);
		}
	return(1);
	}

/* ----------------------------------------------------------------------- */ }
