/*
**	msql_yacc.y	- 
**
**
** Copyright (c) 1993-95  David J. Hughes
** Copyright (c) 1995  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** The software may be modified for your own purposes, but modified versions
** may not be distributed.
**
** This software is provided "as is" without any expressed or implied warranty.
**
** ID = "$Id:"
**
*/

%{
#include <stdio.h>
#include <sys/types.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netdb.h>

#include "msql_priv.h"
#include "msql.h"
#include "common/portability.h"

int	yylineno;
extern	int selectWildcard,
	selectDistinct,
	yytoklen;

ident_t	*msqlCreateIdent();
int	yylex();
void	yyerror();

#define myFree(x) Free(x,__FILE__,__LINE__)

%}

%token  END_OF_INPUT

%token	GE
%token	LE
%token	NE
%token	EQ
%token	GT
%token	LT

%token	CREATE
%token	DROP
%token	INSERT
%token	DELETE
%token	SELECT
%token	UPDATE

%token	ALL
%token	DISTINCT
%token	AS

%token	WHERE
%token	ORDER
%token	FROM
%token	INTO
%token	TABLE
%token	BY
%token	ASC
%token	DESC
%token	LIKE
%token	RLIKE
%token	CLIKE
%token	AND
%token	OR
%token	VALUES
%token	SET

%token	NOT
%token	NULLSYM

%token	PRIMARY
%token	KEY
%token	INDEX
%token	UNIQUE
%token	ON

%token	IDENT
%token	SET_FUNCT
%token	SYS_VAR
%token	TEXT
%token	NUM
%token	REAL_NUM

%token	INT
%token	BOOL
%token	CHAR
%token	TEXT
%token	REAL

%token  LIMIT

%token  CREATE_TABLE
%token  CREATE_INDEX
%token  CREATE_SEQUENCE

%token	DROP_TABLE
%token	DROP_INDEX
%token  DROP_SEQUENCE

%token	SEQUENCE
%token	VALUE
%token	STEP

%token	AVL_INDEX

%%

/*
** High level definitions
**
** Note : The lex input routines return a flag character of \001 to
** 	indicate the end of input.  This allows me to force a query
**	to be terminated at a know point (ie. the end of the query-buf)
**	Without this something like "select * from foo ;" is found by
**	yacc to be a legit query followed by a second query containing
**	only a ';' character.  The flag is generated by msqlInput() and
**	msqlFlexInput() in msql_io.c
*/

query
	: /* NULL */
	| verb_clause END_OF_INPUT
		{	
			msqlProcessQuery();
			msqlClean();
		}

verb_clause
	: create
	| select
	| drop
	| insert
	| update
	| delete


/*
** Create : create database tables
*/


create
	: CREATE create_types


create_types
	: TABLE IDENT '(' 
		{
			command = CREATE_TABLE;
			msqlAddTable($2,NULL);
			myFree($2);
		}
	  field_list ')'
	| opt_unique opt_index_type INDEX IDENT ON IDENT 
		{
			command = CREATE_INDEX;
			msqlAddIndex($4, $6, (int)$1, (int)$2);
			myFree($4);
			myFree($6);
		}
	  '(' index_list ')'
	| SEQUENCE ON IDENT opt_seq_step opt_seq_val
		{
			command = CREATE_SEQUENCE;
			msqlAddSequence($3, (int)$4, (int)$5);
			myFree($3);
		}


index_list
	: IDENT
		{
			if(msqlAddField(msqlCreateIdent(NULL,$1),(int)NULL,0,0,0)<0)
			{
				msqlClean();
				return(-1);
			}
			myFree($1);
		}
	| index_list ',' IDENT
		{
			if(msqlAddField(msqlCreateIdent(NULL,$3),(int)NULL,0,0,0)<0)
			{
				msqlClean();
				return(-1);
			}
			myFree($3);
		}

opt_unique
	: UNIQUE
		{
			$$ = (YYSTYPE) 1;
		}
	| /* NULL */
		{
			$$ = 0;
		}


opt_index_type
	: /* NULL */
		{
			$$ = (YYSTYPE) IDX_AVL;
		}
	| AVL_INDEX
		{
			$$ = (YYSTYPE) IDX_AVL;
		}


opt_seq_step
	: /* NULL */
		{
			$$ = (YYSTYPE) 1;
		}
	| STEP NUM
		{
			$$ = (YYSTYPE) atoi($2);
		}

opt_seq_val
	: /* NULL */
		{
			$$ = (YYSTYPE) 1;
		}
	| VALUE NUM
		{
			$$ = (YYSTYPE) atoi($2);
		}



field_list
	: field_list_item
	| field_list ',' field_list_item



field_list_item
	: qual_ident type opt_nullspec opt_keyspec
		{ 
			if(msqlAddField((ident_t *)$1,(int)$2,arrayLen,
				notnullflag,keyflag)<0)
			{
				msqlClean();
				return(-1);
			}
			if (arrayLen)
			{
				(void)myFree(arrayLen);
			}
			arrayLen = 0;
		}



type
	: INT
		{ 	$$ = (YYSTYPE) INT_TYPE; }
	| REAL
		{ 	$$ = (YYSTYPE) REAL_TYPE; }
	| CHAR '(' NUM ')'
		{ 
			arrayLen = $3; 
		 	$$ = (YYSTYPE) CHAR_TYPE; 
		}
	| TEXT '(' NUM ')'
		{ 
			arrayLen = $3; 
		 	$$ = (YYSTYPE) TEXT_TYPE; 
		}


opt_nullspec
	: /* NULL */	
		{
			notnullflag = 0;
		}
	| NOT NULLSYM	
		{
			notnullflag = 1;
		}


opt_keyspec
	: /* NULL */	
		{
			keyflag = 0;
		}
	| PRIMARY KEY	
		{
			keyflag = 1;
		}




/*
** Select : retrieve data from table
*/


select
	: SELECT select_data FROM table_list where_clause order_clause limit_clause
		{
			command = SELECT;
		}


select_data
	: select_fields

/*
**	| set_functions
*/
	

select_fields
	: dist_qual item_list

dist_qual
	: /* NULL */
		{ selectDistinct = 0; }
	| ALL
		{ selectDistinct = 0; }
	| DISTINCT
		{ selectDistinct = 1; }

item_list
	: item_list ',' select_item
	| select_item
	| '*'
		{
			ident_t	*tmp;

			tmp = msqlCreateIdent(NULL,"*");
			msqlAddField((ident_t *)tmp,0,0,0,0);
			selectWildcard = 1;
		}

select_item
	: qual_ident
		{
			msqlAddField((ident_t *)$1,0,0,0,0);
		}
	| qual_sysvar
		{
			msqlAddField((ident_t *)$1,0,0,0,0);
		}

/*
set_functions
	: set_function
	| set_functions ',' set_function

set_function
	: SET_FUNCT '(' set_funct_param ')'
		{
			if (msqlAddSetFunct($1, (ident_t *)$3) < 0)
			{
				msqlClean();
				return(-1);
			}
		}

set_funct_param
	: qual_ident
		{ $$ = $1; }
	| qual_sysvar
		{ $$ = $1; }
*/
	


table_list
	: IDENT
		{
			msqlAddTable($1,NULL);
			myFree($1);
		}
	| IDENT table_alias IDENT
		{
			msqlAddTable($1, $3);
			myFree($1);
			myFree($3);
		}
	| table_list ',' IDENT
		{
			msqlAddTable($3,NULL);
			myFree($3);
		}
	| table_list ',' IDENT table_alias IDENT
		{
			msqlAddTable($3, $5);
			myFree($3);
			myFree($5);
		}

table_alias
	: /* NULL */
	| AS
	| EQ

where_clause
	: /* NULL */
	| WHERE cond_list


cond_list
	: cond_list cond_cont cond_field cond_op cond_literal
		{ msqlAddCond((ident_t *)$3,(int)$4,(val_t *)$5,(int)$2); } 
	| cond_list cond_cont sub_cond
		{ msqlAddSubCond((int)$2); }
	| cond_field cond_op cond_literal
		{ msqlAddCond((ident_t *)$1,(int)$2,(val_t *)$3,NO_BOOL); }
	| sub_cond
		{ msqlAddSubCond(NO_BOOL); }

sub_cond
	: '(' 
	  	{
			msqlPushCond();
		}
	   cond_list ')'


cond_field
	: qual_ident
		{ $$ = $1; }
	| qual_sysvar
		{ $$ = $1; }

cond_cont
	: AND
		{ $$ = (char *)AND_BOOL; }
	| OR
		{ $$ = (char *)OR_BOOL; }

cond_op
	: EQ
		{ $$ = (char *)EQ_OP; }
	| NE
		{ $$ = (char *)NE_OP; }
	| LT
		{ $$ = (char *)LT_OP; }
	| LE
		{ $$ = (char *)LE_OP; }
	| GT
		{ $$ = (char *)GT_OP; }
	| GE
		{ $$ = (char *)GE_OP; }
	| LIKE
		{ $$ = (char *)LIKE_OP; }
	| RLIKE
		{ $$ = (char *)RLIKE_OP; }
	| CLIKE
		{ $$ = (char *)CLIKE_OP; }
	| NOT LIKE
		{ $$ = (char *)NOT_LIKE_OP; }
	| NOT RLIKE
		{ $$ = (char *)NOT_RLIKE_OP; }
	| NOT CLIKE
		{ $$ = (char *)NOT_CLIKE_OP; }


order_clause
	: 
	| ORDER BY order_list

order_list
	: order_list ',' qual_ident order_dir
		{ msqlAddOrder((ident_t *)$3,(int) $4); }
	| qual_ident order_dir
		{ msqlAddOrder((ident_t *)$1,(int) $2); }

order_dir
	: ASC
		{ $$ = (char *) ASC; }
	| DESC
		{ $$ = (char *) DESC; }
	| /* NULL */
		{ $$ = (char *) ASC; }

limit_clause
	:
		{ msqlSelectLimit = 0; }
	| LIMIT literal
		{ msqlSetSelectLimit((val_t *)$2); }


/*
** Drop : delete entire table
*/

drop
	: DROP drop_types


drop_types
	: TABLE IDENT
		{
			command = DROP_TABLE;
			msqlAddTable($2,NULL);
			myFree($2);
		}
	| INDEX IDENT FROM IDENT
		{
			command = DROP_INDEX;
			msqlAddIndex($2, $4, 0, 0);
			myFree($2);
			myFree($4);
		}
	| SEQUENCE FROM IDENT
		{
			command = DROP_SEQUENCE;
			msqlAddSequence($3,0,0);
			myFree($3);
		}


/*
** Insert : add new data to table
*/

insert
	: INSERT INTO IDENT opt_field_spec 
		{
			command = INSERT;
			msqlAddTable($3,NULL);
			if ($4)
				expandTableFields($3);
			myFree($3);
		}
	  VALUES '(' values ')'


opt_field_spec
	: /* NULL */
		{
			ident_t	*tmp;

			tmp = msqlCreateIdent(NULL,"*");
			msqlAddField((ident_t *)tmp,0,0,0,0);
			$$ = (char *) 1;
		}
	| '(' fields ')'
		{
			$$ = (char *) 0;
		}


fields
	: fields ',' qual_ident
		{ 
			msqlAddField((ident_t *)$3,0,0,0,0);
		}
	| qual_ident
		{ 
			msqlAddField((ident_t *)$1,0,0,0,0); 
		}



values
	: values ',' literal
		{ 
			msqlAddFieldValue((val_t *)$3);
		 }
	|  literal
		{ 
			msqlAddFieldValue((val_t *)$1); 
		}



/*
** Update : replace a table entry
*/

update
	: UPDATE IDENT SET update_list where_clause
		{
			command = UPDATE;
			msqlAddTable((char *)$2,NULL);
			myFree($2);
		}

update_list
	: update_list ',' qual_ident EQ literal
		{ msqlAddField((ident_t *)$3,0,0,0,0);
		  msqlAddFieldValue((val_t *)$5); }
	| qual_ident EQ update_value
		{ msqlAddField((ident_t *)$1,0,0,0,0);
		  msqlAddFieldValue((val_t *) $3); }


update_value
	: literal
		{ $$ = $1; }
	| qual_sysvar
		{ $$ = $1; }

/*
** Delete : conditionally delete table entries (or all entries)
*/

delete
	: DELETE  FROM IDENT where_clause 
		{
			command = DELETE;
			msqlAddTable($3,NULL);
			myFree($3);
		}


/*
** Common definitions
*/

literal
	: TEXT
		{
			$$ = (char *)msqlCreateValue((u_char*)$1,CHAR_TYPE,
				yytoklen);
			(void)myFree($1);
		}
	| NUM
		{
			$$ = (char *)msqlCreateValue((u_char*)$1,INT_TYPE,0);
			(void)myFree($1);
		}
	| REAL_NUM
		{
			$$ = (char *)msqlCreateValue((u_char*)$1,REAL_TYPE,0);
			(void)myFree($1);
		}
	| NULLSYM
		{
			$$ = (char *)msqlCreateValue((u_char*)"null",NULL_TYPE,0);
		}

cond_literal
	: literal
		{
			$$ = $1;
		}
	| qual_ident
		{
			$$ = (char *)msqlCreateValue((u_char *)$1,IDENT_TYPE,0);
		}
	| qual_sysvar
		{
			$$ = (char *)msqlCreateValue((u_char *)$1,SYSVAR_TYPE,0);
		}


qual_ident
	: IDENT
		{ 
			$$ = (char *)msqlCreateIdent(NULL,$1); 
			(void)myFree($1);
			if ($$ == NULL)
			{
				msqlClean();
				return(-1);
			}
		}
	| IDENT '.' IDENT
		{ 
			$$ = (char *)msqlCreateIdent($1,$3); 
			(void)myFree($1);
			(void)myFree($3);
			if ($$ == NULL)
			{
				msqlClean();
				return(-1);
			}
		}

qual_sysvar
	: SYS_VAR
		{ 
			$$ = (char *)msqlCreateIdent(NULL,$1); 
			(void)myFree($1);
			if ($$ == NULL)
			{
				msqlClean();
				return(-1);
			}
		}
	| IDENT '.' SYS_VAR
		{ 
			$$ = (char *)msqlCreateIdent($1,$3); 
			(void)myFree($1);
			(void)myFree($3);
			if ($$ == NULL)
			{
				msqlClean();
				return(-1);
			}
		}
