/*	types.c	- 
**
**
** Copyright (c) 1996  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 <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>

#ifdef HAVE_DIRENT_H
#  include <dirent.h>
#endif

#ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
#endif

#include <common/debug.h>
#include <common/site.h>
#include <common/portability.h>
#include <regexp/regexp.h>



#include "y.tab.h"

#define _MSQL_SERVER_SOURCE
#include "msql_priv.h"
#include "msql.h"
#include "errmsg.h"

#define REG     register
extern	char    errMsg[];




/****************************************************************************
** Regexp related routines
*/


static char     regErrFlag;

void regerror()
{
        regErrFlag++;
}






int likeTest(str,wildstr,length)
        char    *str,
                *wildstr;
        int     length;
{
        int count;
        char *cp1, *cp2;



        cp1 = str;
        cp2 = wildstr;
        count = 0;
        while (count < length)
        {
		if (*cp1 == 0 || *cp2 == 0)
		{
                        if (*cp1==0)
                        {
                                if (*cp2 == 0 ||
                                    (*cp2 == '%' && *(cp2+1) == 0))
                                {
                                        return(1);
                                }
                        }
                        return(0);
		}
                if (*cp2 == '_')
                {
                        cp1++;
                        cp2++;
                        continue;
                }
                if (*cp2 == '%')
                {
                        cp2++;
			while(*cp2 == '%')
				cp2++;
                        if (*cp2 == 0)
                        {
                                return(1);
                        }
			while (*cp2 == '_')
			{
				cp2++;
				cp1++;
				if (*cp1 == 0)
				{
					if (*cp2 == 0)
						return(1);
					else
						return(0);
				}
				count++;
			}
                        while(count < length && *cp1 > 0)
                        {
                                if (*cp1 == *cp2)
                                {
					if(likeTest(cp1,cp2,length-count)==1)
                                        {
                                                return(1);
                                        }
                                }
                                count++;
                                cp1++;
                        }
                        return(0);
                }
		if (*cp2 == '\\')
		{
			cp2++;
		}
                if (*cp1 != *cp2)
                {
                        return(0);
                }
                cp1++;
                cp2++;
                count++;
        }
        return(1);
}




int cLikeTest(str,wildstr,length)
        char    *str,
                *wildstr;
        int     length;
{
        int count;
        char *cp1, *cp2;



        cp1 = str;
        cp2 = wildstr;
        count = 0;
        while (count < length)
        {
		if (*cp1 == 0 || *cp2 == 0)
		{
                        if (*cp1==0)
                        {
                                if (*cp2 == 0 ||
                                    (*cp2 == '%' && *(cp2+1) == 0))
                                {
                                        return(1);
                                }
                        }
                        return(0);
		}
                if (*cp2 == '_')
                {
                        cp1++;
                        cp2++;
                        continue;
                }
                if (*cp2 == '%')
                {
                        cp2++;
			while(*cp2 == '%')
				cp2++;
                        if (*cp2 == 0)
                        {
                                return(1);
                        }
			while (*cp2 == '_')
			{
				cp2++;
				cp1++;
				if (*cp1 == 0)
				{
					if (*cp2 == 0)
						return(1);
					else
						return(0);
				}
				count++;
			}
                        while(count < length && *cp1 > 0)
                        {
                                if (toupper(*cp1) == toupper(*cp2))
                                {
					if(cLikeTest(cp1,cp2,length-count)==1)
                                        {
                                                return(1);
                                        }
                                }
                                count++;
                                cp1++;
                        }
                        return(0);
                }
		if (*cp2 == '\\')
		{
			cp2++;
		}
                if (toupper(*cp1) != toupper(*cp2))
                {
                        return(0);
                }
                cp1++;
                cp2++;
                count++;
        }
        return(1);
}








int rLikeTest(str,re,maxLen)
	char	*str,
		*re;
	int	maxLen;
{
	static	char *lastRE = NULL;
	static	regexp	*reg = NULL;
	int	res;

	if (lastRE == NULL)
	{
		lastRE = (char *)strdup(re);
		reg = NULL;
	}
	else
	{
		if (strcmp(re, lastRE) != 0)
		{
			free(lastRE);
			lastRE = (char *)strdup(re);
			free(reg);
			reg = NULL;
		}
	}
	
	regErrFlag = 0;
	if (!reg)
		reg = regcomp(re);
	res = regexec(reg,str);
	if (regErrFlag)
	{
		strcpy(errMsg, BAD_LIKE_ERROR);
		msqlDebug(MOD_ERR, "Evaluation of LIKE clause failed\n");
		return(-1);
	}
	return(res);
}



/****************************************************************************
** Simple type comparison routines
*/

/****************************************************************************
**      _byteMatch
**
**      Purpose : comparison suite for single bytes.
**      Args    :
**      Returns :
**      Notes   :
**
**
static int byteMatch(v1,v2,op)
        char    v1, v2;
        int     op;
{
        int     result;

        switch(op)
        {
                case EQ_OP:
                        result = (v1 == v2);
                        break;

                case NE_OP:
                        result = (v1 != v2);
                        break;

                case LT_OP:
                        result = (v1 < v2);
                        break;

                case LE_OP:
                        result = (v1 <= v2);
                        break;

                case GT_OP:
                        result = (v1 > v2);
                        break;

                case GE_OP:
                        result = (v1 >= v2);
                        break;
        }
        return(result);
}
**
*/


/* In-lined version */
#define byteMatch(v1,v2,op, result)				\
{								\
        switch(op)						\
        {							\
                case EQ_OP:					\
                        result = ((char)v1 == (char)v2);	\
                        break;					\
                case NE_OP:					\
                        result = ((char)v1 != (char)v2);	\
                        break;					\
                case LT_OP:					\
                        result = ((char)v1 < (char)v2);		\
                        break;					\
                case LE_OP:					\
                        result = ((char)v1 <= (char)v2);	\
                        break;					\
                case GT_OP:					\
                        result = ((char)v1 > (char)v2);		\
                        break;					\
                case GE_OP:					\
                        result = ((char)v1 >= (char)v2);	\
                        break;					\
        }						\
}




/****************************************************************************
** 	_intMatch
**
**	Purpose	: comparison suite for integer fields.
**	Args	: 
**	Returns	: 
**	Notes	: 
**
**
int intMatch(v1,v2,op)
	int	v1, v2, op;
{
	int	result;

	switch(op)
	{
		case EQ_OP:
			result = (v1 == v2);
			break;
			
		case NE_OP:
			result = (v1 != v2);
			break;
			
		case LT_OP:
			result = (v1 < v2);
			break;
			
		case LE_OP:
			result = (v1 <= v2);
			break;
			
		case GT_OP:
			result = (v1 > v2);
			break;
			
		case GE_OP:
			result = (v1 >= v2);
			break;
	}
	return(result);
}
*/

/* In-lined version of intMatch() */

#define intMatch(v1,v2,op,result)			\
{							\
	switch(op)					\
	{						\
		case EQ_OP:				\
			result = (v1 == v2); 		\
			break;				\
		case NE_OP:				\
			result = (v1 != v2);		\
			break;				\
		case LT_OP:				\
			result = (v1 < v2);		\
			break;				\
		case LE_OP:				\
			result = (v1 <= v2);		\
			break;				\
		case GT_OP:				\
			result = (v1 > v2);		\
			break;				\
		case GE_OP:				\
			result = (v1 >= v2);		\
			break;				\
	}						\
}






/****************************************************************************
** 	_charMatch
**
**	Purpose	: Comparison suite for text fields
**	Args	: 
**	Returns	: 
**	Notes	: 
*/

int charMatch(v1,v2,op,maxLen)
	char	*v1,
		*v2;
	int	op,
		maxLen;
{
	int	result,
		cmp;
	REG	char	*c1,*c2;
	REG	int	offset;

	if (op != LIKE_OP && op != RLIKE_OP && op != CLIKE_OP)
	{
		c1 = v1;
		c2 = v2;
		offset=0;
		cmp = 0;
		while(offset < maxLen)
		{
			if ((cmp = *c1 - *c2) != 0)
				break;
			if ( *c1==0 || *c2==0)
				break;
			c1++;
			c2++;
			offset++;
		}
	}
	switch(op)
	{
		case EQ_OP:
			result = (cmp == 0);
			break;
			
		case NE_OP:
			result = (cmp != 0);
			break;
			
		case LT_OP:
			result = (cmp < 0);
			break;
			
		case LE_OP:
			result = (cmp <= 0);
			break;
			
		case GT_OP:
			result = (cmp > 0);
			break;
			
		case GE_OP:
			result = (cmp >= 0);
			break;

		case RLIKE_OP:
			result = rLikeTest(v1,v2,maxLen);
			break;

		case LIKE_OP:
			result = likeTest(v1,v2,maxLen);
			break;

		case CLIKE_OP:
			result = cLikeTest(v1,v2,maxLen);
			break;

		case NOT_RLIKE_OP:
			result = !(rLikeTest(v1,v2,maxLen));
			break;

		case NOT_LIKE_OP:
			result = !(likeTest(v1,v2,maxLen));
			break;

		case NOT_CLIKE_OP:
			result = !(cLikeTest(v1,v2,maxLen));
			break;
	}
	return(result);
}






/****************************************************************************
** 	_realMatch
**
**	Purpose	: Comparison suite for real fields
**	Args	: 
**	Returns	: 
**	Notes	: 
**
**
int realMatch(v1,v2,op)
	double	v1, 
		v2;
	int 	op;
{
	int	result;

	switch(op)
	{
		case EQ_OP:
			result = (v1 == v2);
			break;
			
		case NE_OP:
			result = (v1 != v2);
			break;
			
		case LT_OP:
			result = (v1 < v2);
			break;
			
		case LE_OP:
			result = (v1 <= v2);
			break;
			
		case GT_OP:
			result = (v1 > v2);
			break;
			
		case GE_OP:
			result = (v1 >= v2);
			break;
	}
	return(result);
}
**
*/


/* in-lined version */

#define realMatch(v1,v2,op, result)			\
{							\
	switch(op)					\
	{						\
		case EQ_OP:				\
			result = (v1 == v2);		\
			break;				\
		case NE_OP:				\
			result = (v1 != v2);		\
			break;				\
		case LT_OP:				\
			result = (v1 < v2);		\
			break;				\
		case LE_OP:				\
			result = (v1 <= v2);		\
			break;				\
		case GT_OP:				\
			result = (v1 > v2);		\
			break;				\
		case GE_OP:				\
			result = (v1 >= v2);		\
			break;				\
	}						\
}





/****************************************************************************
** varchar and overflow buffer routines
*/


/****************************************************************************
**      _
**
**      Purpose : 
**      Args    : 
**      Returns : 
**      Notes   :
*/

u_int readOverflowFreeList(cacheEntry,pos)
	cache_t	*cacheEntry;
	u_int	pos;
{
	char	*cp;
	u_int	*next;

	/*
	** Note, skip the leading freelist header
	*/
	cp = cacheEntry->overflowMap + sizeof(u_int) +
		(pos * (OFB_SIZE + sizeof(u_int)));
	next = (u_int *)cp;
	return(*next);
}


int writeOverflowFreeList(cacheEntry,pos,value)
	cache_t	*cacheEntry;
	u_int	pos,
		value;
{
	char	*cp;

	cp = cacheEntry->overflowMap + sizeof(u_int) +
		(pos * (OFB_SIZE + sizeof(u_int)));
	bcopy(&value, cp, sizeof(u_int));
	return(0);
}



u_int popOverflowPos(cacheEntry)
        cache_t *cacheEntry;
{
        u_int   pos,
		*posPtr;

        msqlTrace(TRACE_IN,"popOverflowPos()");
        posPtr = (u_int *)cacheEntry->overflowMap;
        if (!posPtr)
                return(NO_POS);
	pos = *posPtr;
        if (pos != NO_POS)
        {
                *posPtr = readOverflowFreeList(cacheEntry,pos);
        }
        return(pos);
}





int pushOverflowPos(cacheEntry, pos)
        cache_t *cacheEntry;
        u_int   pos;
{
	u_int	*posPtr;

        msqlTrace(TRACE_IN,"pushOverflowPos()");

        posPtr = (u_int *)cacheEntry->overflowMap;
        if ((*posPtr = NO_POS))
        {
                *posPtr = pos;
        }
        else
        {
		if (writeOverflowFreeList(cacheEntry,*posPtr,pos) < 0)
		{
                        return(-1);
                }
        }
        if (writeOverflowFreeList(cacheEntry,pos,NO_POS) < 0)
        {
                return(-1);
        }
        return(0);
}



u_int writeOverflow(entry, pos, lastPos, data, length)
	cache_t	*entry;
	u_int	pos,
		lastPos;
	u_char	*data;
	int	length;
{
	u_char	*cp;
	off_t	offset;
	static  u_char	buf[OFB_SIZE + sizeof(u_int)];

	if (pos != NO_POS)
	{
		cp = (u_char *)entry->overflowMap + sizeof(u_int) +
			(pos * (OFB_SIZE + sizeof(u_int)));
		bcopy(&lastPos, cp, sizeof(u_int));
		bcopy(data, cp + sizeof(u_int), length);
	}
	else
	{
		lseek(entry->overflowFD, 0L, SEEK_END);
		offset = lseek(entry->overflowFD, 0L, SEEK_CUR);
		bzero(buf,sizeof(buf));
		cp = buf;
		bcopy(&lastPos,cp,sizeof(u_int));
		bcopy(data, cp + sizeof(u_int), length);
		write(entry->overflowFD,buf, sizeof(buf));
		entry->remapOverflow = 1;
		pos = (offset - sizeof(u_int)) / (OFB_SIZE + sizeof(u_int));
	}
	return(pos);
}


void readOverflow(entry, pos, nextPos, buf, numBytes)
	cache_t	*entry;
	u_int	pos,
		*nextPos;
	u_char	*buf;
	int	numBytes;
{
	u_char	*cp;

	cp = (u_char *)entry->overflowMap + sizeof(u_int) + 
		(pos * (OFB_SIZE + sizeof(u_int)));
	bcopy(cp, nextPos, sizeof(u_int));
	bcopy(cp + sizeof(u_int), buf, numBytes);
	*(buf + numBytes) = 0;
}
	


u_int writeVarChar(entry, data, length)
	cache_t	*entry;
	u_char	*data;
	int	length;
{
	u_char	*cp;
	int	remain,
		numBytes;
	u_int	pos,
		lastPos;

	remain = strlen((char *)data) - length;
	numBytes = remain % OFB_SIZE;
	lastPos = NO_POS;
	while(remain)
	{
		pos = popOverflowPos(entry);
		cp = data + length + remain - numBytes;
		pos = writeOverflow(entry, pos, lastPos, cp, numBytes);
                msqlDebug(MOD_TEXT,"Wrote %d bytes at overflow %d\n",
			numBytes, pos);
		remain -= numBytes;
		numBytes = OFB_SIZE;
		lastPos = pos;
	}
	return(lastPos);
}




u_char *readVarChar(entry, data, fieldLen)
	cache_t	*entry;
	u_char	*data;
	int	fieldLen;
{
	u_char	*value,
		*cp;
	u_int	pos,
		nextPos;
	int	length,
		numBytes;

	bcopy(data,&length, sizeof(int));
	if (length == 0)
		return((u_char *)strdup(""));
	bcopy(data + sizeof(int) ,&pos, sizeof(u_int));
	value = (u_char *)malloc(length + 1);
	if (!value)
	{
		return(NULL);
	}
	cp = value;
	if (fieldLen > length)
		numBytes = length;
	else
		numBytes = fieldLen;
	bcopy(data + sizeof(int)+ sizeof(u_int), cp, numBytes);
	cp += numBytes;
	length -= numBytes;
	while(pos != NO_POS)
	{
		if (length > OFB_SIZE)
			numBytes = OFB_SIZE;
		else
			numBytes = length;
		readOverflow(entry, pos, &nextPos, cp, numBytes);
                msqlDebug(MOD_TEXT,"Read %d bytes at overflow %d\n",
			numBytes, pos);
		pos = nextPos;
		cp += numBytes;
		length -= numBytes;
	}
	return(value);
}



void deleteVarChar(entry, pos)
	cache_t	*entry;
	u_int	pos;
{
	u_int	nextPos;
	char	buf[OFB_SIZE + 1];

	while(pos != NO_POS)
	{
		readOverflow(entry,pos, &nextPos, buf, OFB_SIZE);
		pushOverflowPos(entry, pos);
		pos = nextPos;
	}
}



int compareVarChar(entry, data1, data2, fieldLen)
	cache_t	*entry;
	u_char	*data1,
		*data2;
	int	fieldLen;
{
	u_char	*cp1, 	*cp2,
		buf1[OFB_SIZE + 1],
		buf2[OFB_SIZE + 1];
	int	count1, count2,
		d1Len, 	d2Len,
		segLen1,segLen2;
	u_int	pos1, 	pos2;

	bcopy(data1,&d1Len, sizeof(int));
	bcopy(data2,&d2Len, sizeof(int));
	if (d1Len == 0 && d2Len == 0)
		return(0);
	if (d1Len == 0)
		return(-1);
	if (d2Len == 0)
		return(1);

	bcopy(data1 + sizeof(int) ,&pos1, sizeof(u_int));
	bcopy(data2 + sizeof(int) ,&pos2, sizeof(u_int));

	cp1 = data1 + sizeof(int) + sizeof(u_int);
	cp2 = data2 + sizeof(int) + sizeof(u_int);
	segLen1 = count1 = d1Len > fieldLen? fieldLen : d1Len;
	segLen2 = count2 = d2Len > fieldLen? fieldLen : d2Len;

	while(d1Len && d2Len)
	{
		while(count1 && count2)
		{
			if (*cp1 == *cp2)
			{
				if (*cp1 == 0)
					return(0);
				count1--;
				count2--;
				cp1++;
				cp2++;
				continue;
			}
			if (*cp1 < *cp2)
				return(-1);
			if (*cp2 < *cp1)
				return(1);
		}
		d1Len -= segLen1;
		d2Len -= segLen2;

		if (d1Len)
		{
			readOverflow(entry, pos1, &pos1, buf1,
				d1Len>OFB_SIZE? OFB_SIZE : d1Len);
			cp1 = buf1;
			count1 = segLen1 = d1Len>OFB_SIZE? OFB_SIZE : d1Len;
		}
		if (d2Len)
		{
			readOverflow(entry, pos2, &pos2, buf2,
				d2Len>OFB_SIZE? OFB_SIZE : d2Len);
			cp2 = buf2;
			count2 = segLen2 = d2Len>OFB_SIZE? OFB_SIZE : d2Len;
		}
	}
	return(0);
}



int matchVarChar(entry,data, cp, length, op)
	cache_t	*entry;
	u_char	*data;
	char	*cp;
	int	length,
		op;
{
	u_char	*cp1, 	*cp2,
		buf[OFB_SIZE + 1];
	int	count,
		dLen,
		segLen,
		cmp,
		result;
	u_int	pos;

	bcopy(data,&dLen, sizeof(int));
	if (dLen == 0 && *cp == 0)
		return(0);
	if (dLen == 0)
		return(-1);
	if (*cp == 0)
		return(1);

	bcopy(data + sizeof(int) ,&pos, sizeof(u_int));

	cp1 = data + sizeof(int) + sizeof(u_int);
	cp2 = (u_char *)cp;
	segLen = count = dLen > length ? length : dLen;
	cmp = 0;
	while(dLen && *cp2)
	{
		while(count)
		{
			if ((cmp = *cp1 - *cp2) != 0)
				break;
			if ( *cp1==0 || *cp2==0)
				break;
			count--;
			cp1++;
			cp2++;
			continue;
		}
		dLen -= segLen;

		if (dLen)
		{
			readOverflow(entry, pos, &pos, buf,
				dLen>OFB_SIZE? OFB_SIZE : dLen);
			cp1 = buf;
			count = segLen = dLen>OFB_SIZE? OFB_SIZE : dLen;
		}
	}
	switch(op)
	{
		case EQ_OP:
			result = (cmp == 0);
			break;
			
		case NE_OP:
			result = (cmp != 0);
			break;

		case LT_OP:
			result = (cmp < 0);
			break;

		case GT_OP:
			result = (cmp > 0);
			break;

		case LE_OP:
			result = (cmp <= 0);
			break;

		case GE_OP:
			result = (cmp >= 0);
			break;

		default:
			strcpy(errMsg, TEXT_REGEX_ERROR);
			return(-1);
	}
	return(result);
}




/****************************************************************************
** Row comparison routines
*/


/****************************************************************************
** 	_matchRow
**
**	Purpose	: Determine if the given row matches the required data
**	Args	: 
**	Returns	: 
**	Notes	: Used by "where" clauses
*/

int matchRow(cacheEntry,row,conds)
	cache_t	*cacheEntry;
	row_t	*row;
	cond_t	*conds;
{
	REG 	cond_t	*curCond;
	REG 	char	*cp,
			*cp2;
	REG 	int	result,
			tmp;
	int	*offset,
		init=1,
		iv;
	double	fv;
	u_char	*data;
	val_t	*value,
		tmpVal;
	field_t	*curField,
		tmpField;
	int	tmpFlist[2],
		foundField;


	msqlTrace(TRACE_IN,"matchRow()");
	/* bzero(&tmpVal, sizeof(tmpVal)); */
	result=0;
	if (!conds)
	{
		msqlTrace(TRACE_OUT,"matchRow()");
		return(1);
	}
	data = row->data;
	curCond = conds;
	offset = conds->clist;
	while(curCond)
	{
		/*
		** If this is a subcond just recurse and continue
		*/
		if (curCond->subCond)
		{
			tmp = matchRow(cacheEntry, row, curCond->subCond);
			if (tmp < 0)
				return(tmp);
			if (init)
			{
				result = tmp;
				init = 0;
			}
			else
			{
                        	switch(curCond->bool)
                        	{
                                	case NO_BOOL:
                                        	result = tmp;
                                        	break;

                                	case AND_BOOL:
                                        	result &= tmp;
                                        	break;

                                	case OR_BOOL:
                                        	result |= tmp;
                                        	break;
                        	}
			}
			curCond = curCond->next;
			continue;
		}



		/*
		** OK, it wasn't a sub cond.  Proceded as normal.
		**
		** If we are comparing 2 fields (e.g. in a join) then
		** grab the value of the second field so that we can do
		** the comparison.  Watch for type mismatches!
		*/
		foundField = 0;
		switch(curCond->value->type)
		{
		    case IDENT_TYPE:
			value = curCond->value;
			curField = cacheEntry->def;
			if (!*(value->val.identVal->seg1))
			{
				if (!cacheEntry->result)
				{
					strcpy(value->val.identVal->seg1,
						cacheEntry->table);
				}
				else
				{
					strcpy(errMsg,UNQUAL_ERROR);
					msqlDebug(MOD_ERR,
					   "Unqualified field in comparison\n");
					msqlTrace(TRACE_OUT,"matchRow()");
					return(-1);
				}
			}
			while(curField)
			{
				if (strcmp(curField->table,
					value->val.identVal->seg1) == 0 &&
				    strcmp(curField->name,
					value->val.identVal->seg2) == 0)
				{
					(void)bcopy(curField,&tmpField,
						sizeof(field_t));
					tmpField.value=NULL;
					tmpField.next = NULL;
					msqlSetupFields(cacheEntry,tmpFlist,
						&tmpField);
					extractValues(cacheEntry,row,&tmpField,
						tmpFlist);
					(void)bcopy(tmpField.value,&tmpVal,
						sizeof(val_t));
					if (tmpVal.type == CHAR_TYPE)
					{
					    tmpVal.val.charVal= (u_char*)
						fastMalloc
						(curField->length + 1);
					    bcopy(tmpField.value->val.charVal,
						tmpVal.val.charVal,
						curField->length);
					    *(tmpVal.val.charVal + 
						curField->length) = 0;
					}
					msqlFreeValue(tmpField.value);
					tmpField.value = NULL;
					value = &tmpVal;
					foundField = 1;
					break;
				}
				curField=curField->next;
			}
			if (!foundField)
			{
				sprintf(errMsg,BAD_FIELD_ERROR,
					value->val.identVal->seg1,
					value->val.identVal->seg2);
				msqlDebug(MOD_ERR,"Unknown field '%s.%s'\n",
					value->val.identVal->seg1,
					value->val.identVal->seg2);
				msqlTrace(TRACE_OUT,"matchRow()");
				(void)free(value->val.charVal);
				return(-1);
			}
			break;

		    case TEXT_TYPE:
			strcpy(errMsg,TEXT_COND_ERROR);
			msqlDebug(MOD_ERR,
				"Can't use TEXT field comparison\n");
			msqlTrace(TRACE_OUT,"matchRow()");
			return(-1);
			break;

		    case INT_TYPE:
		    case REAL_TYPE:
		    case CHAR_TYPE:
		    default:
			value = curCond->value;
			break;
		}


		/*
		** Ensure that the comparison is with the correct type.
		** We can't do this in msqlSetupConds() as we have to wait
		** for the evaluation of field to field comparisons.  We
		** also fudge it for real/int comparisons.
		*/

		if (value->nullVal)
		{
			value->type = curCond->type;
		}
		if (curCond->type == REAL_TYPE  && value->type == INT_TYPE)
		{
			value->val.realVal = value->val.intVal;
			value->type = REAL_TYPE;
		}
		if (curCond->type != value->type && 
		    !(curCond->type == TEXT_TYPE && value->type == CHAR_TYPE))
		{
			sprintf(errMsg,BAD_TYPE_ERROR, curCond->name);
			msqlDebug(MOD_ERR,"Bad type for comparison of '%s'",
				curCond->name);
			return(-1);
		}


		/*
		** O.K. do the actual comparison
		*/
		switch(curCond->type)
		{
			case INT_TYPE:
				if (value->nullVal)
				{
					byteMatch(*(data + *offset),0,
						curCond->op, tmp);
					break;
				}
				if (curCond->sysvar)
				{
					tmp = compareSysVar(cacheEntry,row,
						curCond);
					break;
				}
#ifdef _CRAY
				ival = unpackInt32(data + *offset + 1);
#else

				bcopy4((data + *offset +1),&iv);
#endif
				if (curCond->op == LIKE_OP)
				{
					strcpy(errMsg, INT_LIKE_ERROR);
					msqlDebug(MOD_ERR,
					   "Can't perform LIKE on int value\n");
					msqlTrace(TRACE_OUT,"matchRow()");
					return(-1);
				}
#ifndef _CRAY
				intMatch(iv,value->val.intVal,curCond->op,tmp);
#else
				intMatch(ival,value->val.intVal,curCond->op,
					tmp);
#endif
				break;

			case CHAR_TYPE:
				if (value->nullVal)
				{
					byteMatch(*(data + *offset),0,
						curCond->op, tmp);
					break;
				}
				if (curCond->sysvar)
				{
					tmp = compareSysVar(cacheEntry,row,
						curCond);
					break;
				}
				cp = (char *)data + *offset +1;
				tmp = charMatch(cp,value->val.charVal,
					curCond->op, curCond->length);
				if (value == &tmpVal)
				{
					free(tmpVal.val.charVal);
				}
				if (tmp < 0)
				{
					msqlTrace(TRACE_OUT,"matchRow()");
					return(-1);
				}
				break;

			case TEXT_TYPE:
				if (value->nullVal)
				{
					byteMatch(*(data + *offset),0,
						curCond->op,tmp);
					break;
				}
				cp = (char *)data + *offset +1;
				tmp = matchVarChar(cacheEntry,cp,
					value->val.charVal,
					curCond->length, curCond->op);
				if (value == &tmpVal)
				{
					free(tmpVal.val.charVal);
				}
				if (tmp < 0)
				{
					msqlTrace(TRACE_OUT,"matchRow()");
					return(-1);
				}
				break;

			case REAL_TYPE:
				if (value->nullVal)
				{
					byteMatch(*(data + *offset),0,
						curCond->op,tmp);
					break;
				}
				if (curCond->sysvar)
				{
					tmp = compareSysVar(cacheEntry,row,
						curCond);
					break;
				}
				bcopy8((data + *offset +1),&fv);
				if (curCond->op == LIKE_OP)
				{
					strcpy(errMsg, REAL_LIKE_ERROR);
					msqlDebug(MOD_ERR,
					  "Can't perform LIKE on real value\n");
					msqlTrace(TRACE_OUT,"matchRow()");
					return(-1);
				}
				realMatch(fv,value->val.realVal,
					curCond->op, tmp);
				break;
		}
	
		if (init)
		{
			result = tmp;
			init = 0;
		}
		else
		{
			switch(curCond->bool)
			{
				case NO_BOOL:
					result = tmp;
					break;
	
				case AND_BOOL:
					result &= tmp;
					break;
	
				case OR_BOOL:
					result |= tmp;
					break;
			}
		}
		curCond = curCond->next;
		offset++;
	}
	msqlTrace(TRACE_OUT,"matchRow()");
	return(result);
}




int checkDupRow(entry,data1,data2)
	cache_t	*entry;
	u_char	*data1,
		*data2;
{
	field_t	*curField;
	u_char	*cp1, *cp2;
	int	res,
		offset;


	curField = entry->def;
	res = offset = 0;
	while(curField)
	{
		cp1 = data1+offset+1;
		cp2 = data2+offset+1;
		switch (curField->type)
		{
			case CHAR_TYPE:
			case REAL_TYPE:
			case INT_TYPE:
				res = bcmp(cp1, cp2, curField->dataLength);
				break;

			case TEXT_TYPE:
				res = compareVarChar(entry,cp1,cp2,
					curField->dataLength) == 1;
				break;
		}
		if (res != 0)
			break;
		offset += curField->dataLength + 1;
		curField = curField->next;
	}
	return(res);
}





int compareRows(entry,r1,r2,order,olist)
	cache_t	*entry;
	row_t	*r1,
		*r2;
	order_t	*order;
	int	*olist;
{
	REG 	order_t *curOrder;
	char	buf[sizeof(double)],
		*cp1,
		*cp2;
	u_char	*data1,
		*data2;
	int	res,
		*offset,
		ip1,
		ip2;
	double	fp1,
		fp2;


	/*
	** Allow for cases when rows are not defined
	*/
	msqlTrace(TRACE_IN,"compareRows()");
	if (r1 && !r2)
	{
		msqlTrace(TRACE_OUT,"compareRows()");
		return(-1);
	}
	if (!r1 && r2)
	{
		msqlTrace(TRACE_OUT,"compareRows()");
		return(1);
	}
	if (!r1 && !r2)
	{
		msqlTrace(TRACE_OUT,"compareRows()");
		return(0);
	}

	/*
	** OK, we have both rows.
	*/
	data1 = r1->data;
	data2 = r2->data;
	curOrder = order;
	offset = olist;
	while(curOrder)
	{
		switch(curOrder->type)
		{
			case INT_TYPE:
#ifndef _CRAY
				bcopy4((data1 + *offset +1),buf);
				ip1 = (int) * (int*)buf;
				bcopy4((data2 + *offset +1),buf);
				ip2 = (int) * (int*)buf;

				if (ip1 == ip2)
					res = 0;
				if (ip1 > ip2)
					res = 1;
				if (ip1 < ip2)
					res = -1;
#else
				ival1 = unpackInt32(data1 + *offset + 1);
				ival2 = unpackInt32(data2 + *offset + 1);

				if (ival1 == ival2)
					res = 0;
				if (ival1 > ival2)
					res = 1;
				if (ival1 < ival2)
					res = -1;
#endif
				break;

			case CHAR_TYPE:
				cp1 = (char *)data1 + *offset +1;
				cp2 = (char *)data2 + *offset +1;
				res = strncmp(cp1,cp2,curOrder->length);
				break;

			case REAL_TYPE:
				bcopy8((data1+*offset+1),buf);
				fp1 = (double) * (double *)(buf);
				bcopy8((data2+*offset+1),buf);
				fp2 = (double) * (double *)(buf);
				if (fp1 == fp2)
					res = 0;
				if (fp1 > fp2)
					res = 1;
				if (fp1 < fp2)
					res = -1;
				break;

			case TEXT_TYPE:
				cp1 = (char *)data1 + *offset +1;
				cp2 = (char *)data2 + *offset +1;
				res = compareVarChar(curOrder->entry, cp1, 
					cp2, curOrder->length);
		}
		if (curOrder->dir == DESC)
		{
			res = 0 - res;
		}
		if (res != 0)
		{
			msqlTrace(TRACE_OUT,"compareRows()");
			return(res);
		}
		curOrder = curOrder->next;
		offset++;
	}
	msqlTrace(TRACE_OUT,"compareRows()");
	return(0);
}


	
