/*
**      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>
#ifdef OS2
#  undef  APIENTRY
#  define APIENTRY _Optlink
#  include <arpa/nameser.h>
#else
#  include <arpa/inet.h>
#endif
#include <signal.h>
#include <netdb.h>

#include <time.h>                                          /* OS/2 !!! */

#include "common/portable.h"
#include "msql_prv.h"
#include "msql.h"

int     yylineno;
extern  int selectWildcard,
        selectDistinct,
        yytoklen;
ident_t *msqlCreateIdent();

#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  AND
%token  OR
%token  VALUES
%token  SET

%token  NOT
%token  NULLSYM

%token  PRIMARY
%token  KEY

%token  IDENT
%token  TEXT
%token  NUM
%token  REAL_NUM

%token  INT_
%token  BOOL_
%token  CHAR_
%token  REAL_

%token  LIMIT

%token  SYSDATE
%token  SYSTIME

%%

/*
** 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 TABLE IDENT '('
                {
                        command = CREATE;
                        if (msqlAddTable($3,NULL) < 0)
                        {
                                myFree($3);
                                msqlClean();
                                return;
                        }
                        myFree($3);
                }
          field_list ')'


field_list
        : field_list_item
        | field_list ',' field_list_item



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



type
        : INT_
        | REAL_
        | CHAR_ '(' NUM ')'
                {
                        arrayLen = $3;
                        $$=$1;
                }

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 dist_qual item_list FROM table_list where_clause order_clause limit_clause
                {
                        command = SELECT;
                }

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

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

                        tmp = msqlCreateIdent(NULL,"*");
                        if (msqlAddField(tmp,0,0,0,0) < 0)
                        {
                                msqlClean();
                                return;
                        }
                        selectWildcard = 1;
                }

field
        : qual_ident
                {
                        if (msqlAddField($1,0,0,0,0) < 0)
                        {
                                msqlClean();
                                return;
                        }
                }

table_list
        : IDENT
                {
                        if (msqlAddTable($1,NULL) < 0)
                        {
                                myFree($1);
                                msqlClean();
                                return;
                        }
                        myFree($1);
                }
        | IDENT table_alias IDENT
                {
                        if (msqlAddTable($1, $3) < 0)
                        {
                                myFree($1);
                                myFree($3);
                                msqlClean();
                                return;
                        }
                        myFree($1);
                        myFree($3);
                }
        | table_list ',' IDENT
                {
                        if (msqlAddTable($3,NULL) < 0)
                        {
                                myFree($3);
                                msqlClean();
                                return;
                        }
                        myFree($3);
                }
        | table_list ',' IDENT table_alias IDENT
                {
                        if (msqlAddTable($3, $5) < 0)
                        {
                                myFree($3);
                                myFree($5);
                                msqlClean();
                                return;
                        }
                        myFree($3);
                        myFree($5);
                }

table_alias
        : /* NULL */
        | AS
        | EQ

where_clause
        : /* NULL */
        | WHERE cond_list


cond_list
        : cond_list cond_cont qual_ident cond_op cond_literal
                {
                        if (msqlAddCond($3,$4,$5,$2) < 0)
                        {
                                msqlClean();
                                return;
                        }
                }
        | qual_ident cond_op cond_literal
                {
                        if (msqlAddCond($1,$2,$3,NO_BOOL) < 0)
                        {
                                msqlClean();
                                return;
                        }
                }


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; }
        | NOT LIKE
                { $$ = (char *)NOT_LIKE_OP; }


order_clause
        :
        | ORDER BY order_list

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

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

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


/*
** Drop : delete entire table
*/

drop
        : DROP TABLE IDENT
                {
                        command = DROP;
                        if (msqlAddTable($3,NULL) < 0)
                        {
                                myFree($3);
                                msqlClean();
                                return;
                        }
                        myFree($3);
                }


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

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


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

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


fields
        : fields ',' qual_ident
                {
                        if (msqlAddField($3,0,0,0,0) < 0)
                        {
                                msqlClean();
                                return;
                        }
                }
        | qual_ident
                {
                        if (msqlAddField($1,0,0,0,0) < 0)
                        {
                                msqlClean();
                                return;
                        }
                }



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



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

update
        : UPDATE IDENT SET update_list where_clause
                {
                        command = UPDATE;
                        if (msqlAddTable($2,NULL) < 0)
                        {
                                msqlClean();
                                return;
                        }
                        myFree($2);
                }

update_list
        : update_list ',' qual_ident EQ literal
                {
                        if (msqlAddField($3,0,0,0,0) < 0)
                        {
                                msqlClean();
                                return;
                        }
                        msqlAddFieldValue($5);
                }
        | qual_ident EQ literal
                {
                        if (msqlAddField($1,0,0,0,0) < 0)
                        {
                                msqlClean();
                                return;
                        }
                        msqlAddFieldValue($3);
                }


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

delete
        : DELETE  FROM IDENT where_clause
                {
                        command = DELETE;
                        if (msqlAddTable($3,NULL) < 0)
                        {
                                myFree($3);
                                msqlClean();
                                return;
                        }
                        myFree($3);
                }


/*
** Common definitions
*/

literal
        : TEXT
                {
                        $$ = (char *)msqlCreateValue($1,CHAR_TYPE,yytoklen);
                        (void)myFree($1);
                }
        | NUM
                {
                        $$ = (char *)msqlCreateValue($1,INT_TYPE,0);
                        (void)myFree($1);
                }
        | REAL_NUM
                {
                        $$ = (char *)msqlCreateValue($1,REAL_TYPE,0);
                        (void)myFree($1);
                }
        | NULLSYM
                {
                        $$ = (char *)msqlCreateValue("null",NULL_TYPE,0);
                }
        | SYSDATE                                          /* OS/2 !!! */
                {
                        char           *pszDate = " 1970/01/01";
                        struct tm      *psDateTime;
                        time_t          lTime;
                        time( &lTime );
                        psDateTime = localtime( &lTime );
                        sprintf( pszDate,
                                 " %4d/%02d/%2d",
                                 1900 + psDateTime->tm_year,
                                 1 + psDateTime->tm_mon,
                                 psDateTime->tm_mday
                               );
                        $$ = (char *)msqlCreateValue( pszDate,
                                                      CHAR_TYPE,
                                                      12
                                                    );
                }
        | SYSTIME                                          /* OS/2 !!! */
                {
                        char           *pszTime = " 00:00.00";
                        struct tm      *psDateTime;
                        time_t          lTime;
                        time( &lTime );
                        psDateTime = localtime( &lTime );
                        sprintf( pszTime,
                                 " %2d:%02d.%2d",
                                 psDateTime->tm_hour,
                                 psDateTime->tm_min,
                                 psDateTime->tm_sec
                               );
                        $$ = (char *)msqlCreateValue( pszTime,
                                                      CHAR_TYPE,
                                                      10
                                                    );
                }

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


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