/*
**      msql_proc.c     -
**
**
** 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.
**
** This software is provided "as is" without any expressed or implied warranty.
**
** ID = "$Id:"
**
*/


#ifndef lint
static char RCS_id[] =
        "msql_proc.c,v 1.3 1994/08/19 08:03:15 bambi Exp";
#endif


#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 <common/debug.h>
#include <common/portable.h>

#define MSQL_ADT
#include "msql_prv.h"
#include "msql.h"
#ifdef OS2
#  include "msql_yac.h"
#endif
#ifdef WINNT
#  include "msql_yac.h"
#endif
#ifdef UNIX
#  include "y.tab.h"
#endif

int     command,
        notnullflag,
        keyflag,
        outSock,
        msqlSelectLimit;

char    *curDB,
        *arrayLen;

static  cond_t  *condHead = NULL;

field_t *fieldHead = NULL,
        *lastField = NULL;

static  order_t *orderHead = NULL;

static  tname_t *tableHead = NULL;

static  cond_t  *condTail = NULL;
static  field_t *fieldTail = NULL;
static  order_t *orderTail = NULL;
static  tname_t *tableTail = NULL;
static  int     havePriKey = 0;


#define malloc(L)       Malloc(L,__FILE__,__LINE__)
#define free(A)         Free(A,__FILE__,__LINE__)
#define safeFree(x)     {if (x) { (void) free(x); x = NULL; }}
#define REG             register


extern  char    *packet;


void freeValue(val_t *val);


/****************************************************************************
**      _msqlClean - clean out the internal structures
**
**      Purpose : Free all space and reset structures after a query
**      Args    : None
**      Returns : Nothing
**      Notes   : Updates all public data structures
*/

void msqlClean()
{
        register field_t        *curField, *tmpField;
        register cond_t         *curCond,  *tmpCond;
        register order_t        *curOrder, *tmpOrder;
        register tname_t        *curTable, *tmpTable;

        msqlTrace(TRACE_IN,"msqlClean()");
        command = 0;
        havePriKey = 0;

        /*
        ** blow away the table list from the query
        */
        curTable = tableHead;
        while(NULL != curTable)
        {
                tmpTable = curTable;
                curTable = curTable->next;
                (void)free(tmpTable);
        }


        /*
        ** blow away the field list from the query
        */
        curField = fieldHead;
        while(NULL != curField)
        {
                freeValue(curField->value);
                curField->value = NULL;                    /* OS/2 !!! */
                tmpField = curField;
                curField = curField->next;
                (void)free(tmpField);
        }

        /*
        ** Blow away the condition list from the query
        */
        curCond = condHead;
        while(NULL != curCond)
        {
                freeValue(curCond->value);                 /* OS/2 !!! */
                curCond->value = NULL;
                curCond->op = curCond->bool = 0;
                tmpCond = curCond;
                curCond = curCond->next;
                (void)free(tmpCond);
        }
        curOrder = orderHead;
        while(NULL != curOrder)
        {
                curOrder->dir = 0;
                tmpOrder = curOrder;
                curOrder = curOrder->next;
                (void)free(tmpOrder);
        }


        /*
        ** Reset the list pointers
        */

        condHead = condTail = (cond_t *) NULL;
        fieldHead = fieldTail = lastField = (field_t *) NULL;
        orderHead = orderTail = (order_t *) NULL;
        tableHead = tableTail = (tname_t *) NULL;

        msqlBackendClean();

        msqlTrace(TRACE_OUT,"msqlClean()");
}



ident_t *msqlCreateIdent(seg1,seg2)
        char    *seg1,
                *seg2;
{
        ident_t *new;

        msqlTrace(TRACE_IN,"msqlCreateIdent()");
        if (seg1)
        {
                if (strlen(seg1) > NAME_LEN)
                {
                        sprintf(packet,
                                "-1:Identifier name '%s' too long\n",seg1);
                        writePkt(outSock);
                        msqlTrace(TRACE_OUT,"msqlCreateIdent()");
                        return(NULL);
                }
        }
        if (seg2)
        {
                if (strlen(seg2) > NAME_LEN)
                {
                        sprintf(packet,
                                "-1:Identifier name '%s' too long\n",seg2);
                        writePkt(outSock);
                        msqlTrace(TRACE_OUT,"msqlCreateIdent()");
                        return(NULL);
                }
        }
        new = (ident_t *)malloc(sizeof(ident_t));
        if (seg1)
        {
                (void)strcpy(new->seg1,seg1);
        }
        if (seg2)
        {
                (void)strcpy(new->seg2,seg2);
        }
        msqlTrace(TRACE_OUT,"msqlCreateIdent()");
        return(new);
}



static u_char expandEscape(c,remain)
        u_char  *c;
        int     remain;
{
        u_char  ret;

        switch(*c)
        {
                case 'n':
                        ret = '\n';
                        break;
                case 't':
                        ret = '\t';
                        break;
                case 'r':
                        ret = '\r';
                        break;
                case 'b':
                        ret = '\b';
                        break;
                default:
                        ret = *c;
                        break;
        }
        return(ret);
}



val_t *msqlCreateValue(textRep,type,tokLen)
        u_char  *textRep;
        int     type,
                tokLen;
{
        val_t   *new;
        int     length,
                remain,
                match;
        REG     u_char  *cp,
                        *cp2;
        static  u_char nullVal[] = "null";

        msqlTrace(TRACE_IN,"msqlCreateValue()");
        new = (val_t *)malloc(sizeof(val_t));
        new->type = type;
        new->dataLen = tokLen;
        switch(type)
        {
                case NULL_TYPE:
                        new->nullVal = 1;
                        break;
                case IDENT_TYPE:
                        new->val.identVal = (ident_t *)textRep;
                        break;

                case CHAR_TYPE:
                        remain = length = tokLen - 2;
                        new->val.charVal = (u_char *)malloc(length+1);
                        cp = textRep+1;
                        cp2 = new->val.charVal;
                        while(remain)
                        {
                                if (*cp == '\\')
                                {
                                        remain--;
                                        *cp2 = expandEscape(++cp,remain);
                                        if (*cp2)
                                        {
                                                cp2++;
                                                cp++;
                                                remain--;
                                        }
                                }
                                else
                                {
                                        *cp2++ = *cp++;
                                        remain--;
                                }
                        }
                        break;

                case INT_TYPE:
                        new->val.intVal = atoi(textRep);
                        break;

                case REAL_TYPE:
                        sscanf((char *)textRep ,"%lg",&new->val.realVal);
                        break;
        }
        msqlTrace(TRACE_OUT,"msqlCreateValue()");
        return(new);
}


val_t *fillValue(val,type,length)
        char    *val;
        int     type,
                length;
{
        val_t   *new;

        msqlTrace(TRACE_IN,"fillValue()");
        new = (val_t *)malloc(sizeof(val_t));
        new->type = type;
        new->nullVal = 0;
        switch(type)
        {
                case CHAR_TYPE:
                        new->val.charVal = (u_char *)malloc(length+1);
                        (void)bcopy(val,new->val.charVal,length);
                        break;

                case INT_TYPE:
#ifndef _CRAY
                        new->val.intVal = (int) * (int *)val;
#else
                        new->val.intVal = unpackInt32(val);
#endif
                        break;

                case REAL_TYPE:
                        new->val.realVal = (double) * (double *)val;
                        break;
        }
        msqlTrace(TRACE_OUT,"fillValue()");
        return(new);
}


val_t *nullValue()
{
        val_t   *new;

        new = (val_t *)malloc(sizeof(val_t));
        new->nullVal = 1;
        return(new);
}



void freeValue(val)
        val_t   *val;
{
        msqlTrace(TRACE_IN,"freeValue()");
        if (!val)
        {
                msqlTrace(TRACE_OUT,"freeValue()");
                return;
        }
        switch(val->type)
        {
                case IDENT_TYPE:
                        (void)free(val->val.identVal);
                        break;
                case CHAR_TYPE:
                        if (!val->nullVal)
                                (void)free(val->val.charVal);
                        break;
                default:
                        break;
        }
        (void)free(val);
        msqlTrace(TRACE_OUT,"freeValue()");
}



/****************************************************************************
**      _msqlAddField - add a field definition to the list
**
**      Purpose : store field details from the query for later use
**      Args    : field name, field type, field length, value
**      Returns : Nothing
**      Notes   : Depending on the query in process, only some of the
**                args will be supplied.  eg. a SELECT doesn't use the
**                type arg.  The length arg is only used during a create
**                if the field is of type CHAR
*/

int msqlAddField(ident,type,length,notNull,priKey)
        ident_t *ident;
        int     type;
        char    *length;
        int     notNull,
                priKey;
{
        register field_t        *new;
        char    *name,
                *table;

        msqlTrace(TRACE_IN,"msqlAddField()");

        name = ident->seg2;
        table = ident->seg1;


        /*
        ** Look for duplicate field names on a table create
        */
        if (type != 0)
        {
                new = fieldHead;
                while(new)
                {
                        if (strcmp(new->name,name) == 0)
                        {
                                sprintf(packet,
                                        "-1:Duplicate field name '%s'\n",
                                        name);
                                writePkt(outSock);
                                msqlTrace(TRACE_OUT,"msqlAddField()");
                                return(-1);
                        }
                        new = new->next;
                }
        }

        if (priKey)
        {
                if (havePriKey)
                {
                        sprintf(packet,"-1:Can't have multiple primary keys\n");
                        writePkt(outSock);
                        msqlTrace(TRACE_OUT,"msqlAddField()");
                        return(-1);

                }
                else
                {
                        havePriKey = 1;
                }
        }


        new = (field_t *)malloc(sizeof(field_t));
        if (table)
        {
                if (strlen(table) > NAME_LEN)
                {
                        sprintf(packet, "-1:Table name '%s' is too long\n",
                                table);
                        writePkt(outSock);
                        msqlTrace(TRACE_OUT,"msqlAddField()");
                        return(-1);
                }
                (void)strcpy(new->table,table);
        }
        if (strlen(name) > NAME_LEN)
        {
                sprintf(packet, "-1:Field name '%s' is too long\n", name);
                writePkt(outSock);
                msqlTrace(TRACE_OUT,"msqlAddField()");
                return(-1);
        }
        (void)strcpy(new->name,name);
        if (notNull)
        {
                new->flags |= NOT_NULL_FLAG;
        }
        if (priKey)
        {
                new->flags |= PRI_KEY_FLAG;
                new->flags |= NOT_NULL_FLAG;
        }
        switch(type)
        {
                case INT_:
                        new->type = INT_TYPE;
                        new->length = 4;
                        break;

                case CHAR_:
                        new->type = CHAR_TYPE;
                        new->length = atoi(length);
                        break;

                case REAL_:
                        new->type = REAL_TYPE;
                        new->length = sizeof(double);
                        break;

                default:
                        new->type = 0;
                        new->length = 0;
                        break;
        }
        new->next = NULL;
        if (NULL == fieldHead)
        {
                fieldHead = fieldTail = new;
        }
        else
        {
                fieldTail->next = new;
                fieldTail = new;
        }
        free(ident);
        msqlTrace(TRACE_OUT,"msqlAddField()");
        return(0);
}

void msqlSetSelectLimit(value)
        val_t   *value;
{
        msqlSelectLimit = value->val.intVal;
}


void msqlAddFieldValue(value)
        val_t   *value;
{
        register field_t        *fieldVal;
        u_char  *buf;

        msqlTrace(TRACE_IN,"msqlAddFieldValue()");
        if (!lastField)
        {
                lastField = fieldVal = fieldHead;
        }
        else
        {
                fieldVal = lastField->next;
                lastField = lastField->next;
        }
        if (fieldVal)
        {
                if (fieldVal->type == CHAR_TYPE)
                {
                        buf = (u_char *)malloc(fieldVal->length+1);
                        bcopy(value->val.charVal,buf,value->dataLen);
                        free(value->val.charVal);
                        value->val.charVal = buf;
                }
                if (fieldVal->value)
                        freeValue(fieldVal->value);
                fieldVal->value = value;
        }
        msqlTrace(TRACE_OUT,"msqlAddFieldValue()");
}




/****************************************************************************
**      _msqlAddCond  -  add a conditional spec to the list
**
**      Purpose : Store part of a "where_clause" for later use
**      Args    : field name, test op, value, bool (ie. AND | OR)
**      Returns : Nothing
**      Notes   : the BOOL field is only provided if this is not the first
**                element of a where_clause.
*/

int msqlAddCond(ident,op,value,bool)
        ident_t *ident;
        int     op;
        val_t   *value;
        int     bool;
{
        register cond_t *new;
        char    *name,
                *table;

        msqlTrace(TRACE_IN,"msqlAddCond()");

        if (*(ident->seg2))
        {
                name = ident->seg2;
                table = ident->seg1;
        }
        else
        {
                name = ident->seg1;
                table = NULL;
        }

        if (strlen(name) > NAME_LEN)
        {
                sprintf(packet, "-1:Field name '%s' is too long\n",
                        name);
                writePkt(outSock);
                msqlTrace(TRACE_OUT,"msqlAddCond()");
                return(-1);
        }
        if (table)
        {
                if (strlen(table) > NAME_LEN)
                {
                        sprintf(packet, "-1:Table name '%s' is too long\n",
                                name);
                        writePkt(outSock);
                        msqlTrace(TRACE_OUT,"msqlAddCond()");
                        return(-1);
                }
        }
        new = (cond_t *)malloc(sizeof(cond_t));
        (void)strcpy(new->name,name);
        if (table)
        {
                (void)strcpy(new->table,table);
        }
        else
        {
                new->table[0] = '\0';
        }

        new->op = op;
        new->bool = bool;
        new->value = value;
        new->next = NULL;

        if (NULL == condHead)
        {
                condHead = condTail = new;
        }
        else
        {
                condTail->next = new;
                condTail = new;
        }
        free(ident);
        msqlTrace(TRACE_OUT,"msqlAddCond()");
        return(0);
}



/****************************************************************************
**      _msqlAddOrder  -  add an order definition to the list
**
**      Purpose : Store part of an "order_clause"
**      Args    : field name, order direction (ie. ASC or DESC)
**      Returns : Nothing
**      Notes   :
*/

void msqlAddOrder(ident,dir)
        ident_t *ident;
        int     dir;
{
        register order_t        *new;

        msqlTrace(TRACE_IN,"msqlAddOrder()");

        new = (order_t *)malloc(sizeof(order_t));
        if (*ident->seg1)
        {
                (void)strcpy(new->table,ident->seg1);
        }
        else
        {
                new->table[0] = '\0';
        }
        (void)strcpy(new->name,ident->seg2);
        new->dir  = dir;
        new->next = NULL;
        if (!orderHead)
        {
                orderHead = orderTail = new;
        }
        else
        {
                orderTail->next = new;
                orderTail = new;
        }
        free(ident);
        msqlTrace(TRACE_OUT,"msqlAddOrder()");
}




int msqlAddTable(name,alias)
        char    *name,
                *alias;
{
        register tname_t        *new,
                                *cur;

        msqlTrace(TRACE_IN,"msqlAddTable()");

        new = (tname_t *)malloc(sizeof(tname_t));
        if (alias)
        {
                /*
                ** Ensure the alias is unique
                */
                cur = tableHead;
                while(cur)
                {
                        if (strcmp(cur->name,alias) == 0)
                        {
                                sprintf(packet,
                                        "-1:Nonunique table/alias name '%s'\n",
                                        alias);
                                writePkt(outSock);
                                free(new);                 /* OS/2 !!! */
                                msqlTrace(TRACE_OUT,"msqlAddTable()");
                                return(-1);
                        }
                        cur = cur->next;
                }

                /*
                ** Further checks on the table/alias name
                */
                if (strlen(name) > NAME_LEN)
                {
                        sprintf(packet, "-1:Table name '%s' is too long\n",
                                name);
                        writePkt(outSock);
                        free(new);                         /* OS/2 !!! */
                        msqlTrace(TRACE_OUT,"msqlAddTable()");
                        return(-1);
                }
                if (strlen(alias) > NAME_LEN)
                {
                        sprintf(packet,"-1:Table alias name '%s' is too long\n",
                                name);
                        writePkt(outSock);
                        free(new);                         /* OS/2 !!! */
                        msqlTrace(TRACE_OUT,"msqlAddTable()");
                        return(-1);
                }
                (void)strcpy(new->name,alias);
                (void)strcpy(new->cname,name);
        }
        else
        {
                /*
                ** Ensure the table is unique
                */
                cur = tableHead;
                while(cur)
                {
                        if (strcmp(cur->name,name) == 0)
                        {
                                sprintf(packet,
                                        "-1:Nonunique table name '%s'\n",
                                        name);
                                writePkt(outSock);
                                free(new);                 /* OS/2 !!! */
                                msqlTrace(TRACE_OUT,"msqlAddTable()");
                                return(-1);
                        }
                        cur = cur->next;
                }

                if (strlen(name) > NAME_LEN)
                {
                        sprintf(packet, "-1:Table name '%s' is too long\n",
                                name);
                        writePkt(outSock);
                        free(new);                         /* OS/2 !!! */
                        msqlTrace(TRACE_OUT,"msqlAddTable()");
                        return(-1);
                }
                (void)strcpy(new->name,name);
                *(new->cname) = 0;
        }
        new->next = NULL;
        if (!tableHead)
        {
                tableHead = tableTail = new;
        }
        else
        {
                tableTail->next = new;
                tableTail = new;
        }
        msqlTrace(TRACE_OUT,"msqlAddTable()");
        return(0);
}







void msqlSetDB(db)
        char    *db;
{
        curDB = db;
}



/****************************************************************************
**      _msqlProcessQuery  -  send to query to the approp routine
**
**      Purpose : Call the required routine to build the native query
**      Args    : None
**      Returns : Nothing
**      Notes   : Global command variable used.  This is called when the
**                end of an individual query is found in the miniSQL code
**                and provides the hooks into the per-database routines.
*/



void msqlProcessQuery()
{
        int     res;

        msqlTrace(TRACE_IN,"msqlProcessQuery()");
        if (!curDB)
        {
                sprintf(packet,"-1:No current database\n");
                writePkt(outSock);
                msqlDebug(MOD_ERR,"No current database\n");
                msqlTrace(TRACE_OUT,"msqlProcessQuery()");
                return;
        }
        switch(command)
        {
                case SELECT:
                        if (!msqlCheckPerms(READ_ACCESS))
                        {
                                sprintf(packet,"-1:Access Denied\n");
                                writePkt(outSock);
                                msqlTrace(TRACE_OUT,"msqlProcessQuery()");
                                return;
                        }
                        res = msqlSelect(tableHead,fieldHead,condHead,
                                orderHead,curDB);
                        break;
                case CREATE:
                        if (!msqlCheckPerms(WRITE_ACCESS))
                        {
                                sprintf(packet,"-1:Access Denied\n");
                                writePkt(outSock);
                                msqlTrace(TRACE_OUT,"msqlProcessQuery()");
                                return;
                        }
                        res = msqlCreate(tableHead->name,fieldHead,curDB);
                        break;
                case UPDATE:
                        if (!msqlCheckPerms(RW_ACCESS))
                        {
                                sprintf(packet,"-1:Access Denied\n");
                                writePkt(outSock);
                                msqlTrace(TRACE_OUT,"msqlProcessQuery()");
                                return;
                        }
                        res = msqlUpdate(tableHead->name,fieldHead,condHead,
                                curDB);
                        break;
                case INSERT:
                        if (!msqlCheckPerms(WRITE_ACCESS))
                        {
                                sprintf(packet,"-1:Access Denied\n");
                                writePkt(outSock);
                                msqlTrace(TRACE_OUT,"msqlProcessQuery()");
                                return;
                        }
                        res = msqlInsert(tableHead->name,fieldHead,curDB);
                        break;
                case DELETE:
                        if (!msqlCheckPerms(WRITE_ACCESS))
                        {
                                sprintf(packet,"-1:Access Denied\n");
                                writePkt(outSock);
                                msqlTrace(TRACE_OUT,"msqlProcessQuery()");
                                return;
                        }
                        res = msqlDelete(tableHead->name,condHead,curDB);
                        break;
                case DROP:
                        if (!msqlCheckPerms(WRITE_ACCESS))
                        {
                                sprintf(packet,"-1:Access Denied\n");
                                writePkt(outSock);
                                msqlTrace(TRACE_OUT,"msqlProcessQuery()");
                                return;
                        }
                        res = msqlDrop(tableHead->name,curDB);
                        break;
        }
        if (res < 0)
        {
                extern  char    errMsg[];
                char    errBuf[180];

                sprintf(packet,"-1:%s\n",errMsg);
                msqlTrace(TRACE_OUT,"msqlProcessQuery()");
                writePkt(outSock);
        }
        msqlTrace(TRACE_OUT,"msqlProcessQuery()");
}


void msqlQueryOverrunError(txt)
        char    *txt;
{

        msqlTrace(TRACE_IN,"msqlQueryOverrunError()");
        sprintf(packet,"-1:Syntax error.  Bad text after query. '%s'\n",txt);
        writePkt(outSock);
        msqlTrace(TRACE_OUT,"msqlQueryOverrunError()");
}




/* char *msqlParseQuery(inBuf,sock) */
void msqlParseQuery(inBuf,sock)
        char    *inBuf;
        int     sock;
{
        msqlTrace(TRACE_IN,"msqlParseQuery()");
        outSock = sock;
        msqlInitScanner(inBuf);
        yyparse();
        msqlTrace(TRACE_OUT,"msqlParseQuery()");
}

