#.TH SqlParser Onyx
#.SH NAME
#	SqlParser \- Base grammar for SQL's

include "unistd.h"

class SqlParser {
	char		*sql_database;

	Text		*sql_results;

	int		sql_row_type;	/* 0 = Backup no Rows
					   1 = Rows in file
					   2 = Rows in Text
					*/

	Text		*sql_row_text;
	int		sql_row_fd;
	LineStream	*sql_row_stream;

	SqlParser();
	virtual ~SqlParser( );

	virtual int	message(char *str,
		char *s1=0, char *s2=0, char *s3=0,
		char *s4=0, char *s5=0, char *s6=0,
		char *s7=0, char *s8=0, char *s9=0
		);
	virtual int	error(
		char *s1=0, char *s2=0, char *s3=0,
		char *s4=0, char *s5=0, char *s6=0,
		char *s7=0, char *s8=0, char *s9=0
		);

	virtual int		sql();
	virtual int		statement();
	virtual int		database();

	virtual int		sql_new(LineStream *Init);
	virtual int		sql_new(Text *Init);
	virtual int		sql_new(char *Init);

	virtual int		sql_open();
	virtual int		sql_close();

	virtual int		sql_up();

	virtual void		sql_purge();
	virtual void		sql_read();

	virtual Text		*sql_getresult();
	virtual LineStream	*sql_getstream();
	virtual char		*sql_getrow();
	virtual int		sql_getnum();
	}

#.SH DESCRIPTION
#	SqlParser is the base class of the Sql's used in Onyx. It definies the
#	things nessesary in any sql, and the global handling of all sql's.
#
#	Normal way to access a sql from C++ is to create a sqlparser (normaly
#	SqlManager or SqlClient) and give him the transactions by calling
#	sql_new, which can handle LineStreams, Text, and Strings.
#
#	Y now can check the results and errors by calling sql_getresults().
#	Y can check the number of rows affected by calling sql_getnum().
#	Y can get the rows retrieved by a select statement by calling
#	sql_getstream() if Y want them all at a time, or calling sql_getrow()
#	if Y prefer to get them one after the other.
#
#	Y can call sql_up() anytime to check it Yr parser is connected to a
#	running database engine.
#
#	Transactions are written in what I call a BASIC SQL.
#	Y also can call it a cripled SQL, so if Y need a ANSI-SQL3 buy it,
#	dont flame be because my parsers are not ANSI compilant.
#
#	Any Statement have to end with a semicolon.
#
#	SqlParser is nearly doing nothing. Only a few non terminals
#	are defined here, and can be used or overloaded by child classes.

sql :	{	sql_purge();
		}

	statement ";" {
	  	if (sql_up())
	  		sql_read();
		else	message("Error: SQL is down.");
		}

	| true {
	  	message("error:illegal statement");
  		}
	;

#
#	This first purges the parsers from old errors, results or rows
#	retrieved. Now checks if it is a valid statement, or give an
#	error.

statement :
	database
	;

#
#	Only database is a valid statement here.

database :
	"database" matchexp {
		if (sql_database && !sql_close()) {
			error("cannot close ",sql_database);
			return(0);
			}
		else {
			sql_database = strdup(p_matched->lines[0]);
			}

		sql_open();
		}
	| "connect" matchexp {
		if (sql_database && !sql_close()) {
			error("cannot close ",sql_database);
			return(0);
			}
		else {
			sql_database = strdup(p_matched->lines[0]);
			}

		sql_open();
		}
	| "disconnect" matchexp {
		if (sql_database && !sql_close()) {
			error("cannot close ",sql_database);
			return(0);
			}
		if (sql_database) {
			free(sql_database);
			sql_database = 0;
			}
		}
	.

#
#	This checks if a statement begins with "database" and trys to
#	connect to the database calling sql_open().
#
#	Also "connect" and "disconnect" are valid here.

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

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

SqlParser::SqlParser() : Parser()
{	sql_database = 0;

	sql_results = new Text(16);
	sql_row_text = new Text(16);

	sql_row_type = 0;
	sql_row_fd = 0;
	sql_row_stream = 0;
	}

SqlParser::~SqlParser()
{	sql_purge();

	if (sql_database) sql_close();

	delete sql_results;
	delete sql_row_text;
	}

int SqlParser::message(char *str,
		char *s1, char *s2, char *s3,
		char *s4, char *s5, char *s6,
		char *s7, char *s8, char *s9)
{	char line[1025];
	char *p,*q;

	if (str && *str) {
		sprintf(line,str,s1,s2,s3,s4,s5,s6,s7,s8,s9);

		debug(line);

		if (p_fatal) return(0);

		p=line;
		do {	q=strchr(p,'\n');

			if (q) *q=0;
			if (p && *p) sql_results->add_line(p);
			if (q) *q='\n';

			} while (p=q ? q+1 : 0);
		}

	return(1);
	}

int SqlParser::error(
	char *s1,char *s2,char *s3,
	char *s4,char *s5,char *s6,
	char *s7,char *s8,char *s9)
{	if (p_fatal  ) return(0);

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

	if (s1) {
		message("%s %s %s %s %s %s",
			s1,
			s2 ? s2 : "",
			s3 ? s3 : "",
			s4 ? s4 : "",
			s5 ? s5 : "",
			s6 ? s6 : "");
		}
	else	message("error:unknown");

	p_fatal = 1;

	return(0);
	}

int SqlParser::sql_open()
{	if (sql_database) return(1);
	return(0);
	}

int SqlParser::sql_close()
{	if (sql_database) {
		free(sql_database);
		sql_database = 0;
		return(1);
		}

	sql_purge();
	return(0);
	}

int SqlParser::sql_up()
{	if (sql_database)
		return(1);

	return(0);
	}

void SqlParser::sql_purge()
{	/*
	fprintf(stderr,"SQL_PURGE %d %d %d\n",this,sql_row_stream,sql_row_fd);
	*/

	sql_results->purge();
	sql_row_text->purge();

	if (sql_row_stream) {
		delete sql_row_stream;
		sql_row_stream = 0;
		}

	if (sql_row_fd>0) {
		close(sql_row_fd);
		sql_row_fd = 0;
		}
	}

void SqlParser::sql_read() { }

Text *SqlParser::sql_getresult()
{	return(sql_results);
	}

LineStream *SqlParser::sql_getstream()
{	if (!sql_row_stream) {
		switch (sql_row_type) {
		    case 0 :
			return(0);
		    case 1 :
			if (sql_row_fd>0)
				sql_row_stream =
					new FileStream(sql_row_fd,O_RDONLY);
			break;
		    case 2 :
			if (sql_row_text)
				sql_row_stream =
					new TextStream(sql_row_text,O_RDONLY);
			break;
		    }
		}
	return(sql_row_stream);
	}

char *SqlParser::sql_getrow()
{	char line[1024];
	int  ret;

	if (!sql_row_type) return(0);

	if (!sql_row_stream) sql_getstream();
	if (!sql_row_stream) return(0);

	ret=sql_row_stream->readline(line,1024);
	if (ret>=0) return(strdup(line));

	return(0);
	}

int SqlParser::sql_getnum()
{	return(0);
	}

int SqlParser::sql_new(LineStream *Init)
{	NewStream(Init);
	return(sql());
	}

int SqlParser::sql_new(Text *Init)
{	NewStream(Init);
	return(sql());
	}

int SqlParser::sql_new(char *Init)
{	NewStream(Init);
	return(sql());
	}

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