/*	msqldb.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.
**
** 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 <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifdef OS2
#  include <io.h>
#  include <types.h>
#else
#  include <unistd.h>
#  include <arpa/inet.h>
#  include <string.h>
#endif

#ifdef OS2
#  include <common/mman.h>
#else
#  include <sys/mman.h>
#endif

#ifdef HAVE_DIRENT_H
#  ifdef OS2
#    include <common/dirent.h>
#  else
#    include <dirent.h>
#  endif
#endif

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

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

#ifdef OS2
#  include "msql_yacc.h"
#else
#  include "y.tab.h"
#endif

#define	_MSQL_SERVER_SOURCE

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

#define	REG		register





cache_t	tableCache[CACHE_SIZE];

extern	int	outSock;
extern	char	*packet;

int	selectWildcard = 0,
	selectDistinct = 0;

char	errMsg[200];
char	*msqlHomeDir;

cache_t *loadTableDef();




void initBackend()
{
	(void) bzero(tableCache, sizeof(tableCache));
}







/****************************************************************************
** 	_msqlListDBs
**
**	Purpose	: Send a list of available databases to the client
**	Args	: Client socket
**	Returns	: -1 on error
**	Notes	: The only things in the top level data directory are
**		  database directories so we just send a file listing.
*/

void msqlServerListDBs(sock)
	int	sock;
{
	char	path[MAXPATHLEN];
	DIR	*dirp;
#ifdef HAVE_DIRENT
	struct	dirent *cur;
#else
	struct	direct *cur;
#endif

	msqlTrace(TRACE_IN,"msqlListDBs()");
	(void)sprintf(path,"%s/msqldb",msqlHomeDir);
	dirp = opendir(path);
	if (!dirp)
	{
		sprintf(errMsg,BAD_DIR_ERROR,path);
		msqlDebug(MOD_ERR,"Can't open directory \"%s\"\n",path);
		msqlTrace(TRACE_OUT,"msqlListDBs()");
		return;
	}
	
	/*
	** Grab the names dodging any . files
	*/

	cur = readdir(dirp);
	while(cur)
	{
		if (*cur->d_name == '.')
		{
			cur = readdir(dirp);
			continue;
		}
		sprintf(packet,"%d:%s\n",strlen(cur->d_name),cur->d_name);
		writePkt(sock);
		cur = readdir(dirp);
	}
	sprintf(packet,"-100:\n");
	writePkt(sock);
	closedir(dirp);
	msqlTrace(TRACE_OUT,"msqlListDBs()");
	return;
}






/****************************************************************************
** 	_msqlListTables
**
**	Purpose	: Send a list of available tables for a given DB
**	Args	: Client socket and Database name
**	Returns	: -1 on error
**	Notes	: Looks for table definitions files (*.def)
*/

void msqlServerListTables(sock,db)
	int	sock;
	char	*db;
{
	char	path[MAXPATHLEN],
		*cp;
	DIR	*dirp;
#ifdef HAVE_DIRENT
	struct	dirent *cur;
#else
	struct	direct *cur;
#endif

	msqlTrace(TRACE_IN,"msqlListTables()");
	(void)sprintf(path,"%s/msqldb/%s",msqlHomeDir,db);
	dirp = opendir(path);
	if (!dirp)
	{
		sprintf(errMsg,BAD_DIR_ERROR,path);
		msqlDebug(MOD_ERR,"Can't open directory \"%s\"\n",path);
		msqlTrace(TRACE_OUT,"msqlListTables()");
		return;
	}
	
	/*
	** Skip over '.' and '..'
	*/
	cur = readdir(dirp);
	while(cur)
	{
		if (*cur->d_name != '.')
			break;
		cur = readdir(dirp);
	}

	/*
	** Grab the names
	*/

	while(cur)
	{
		cp = (char *)rindex(cur->d_name,'.');
		if (!cp)
		{
			cur = readdir(dirp);
			continue;
		}
	
		if (strcmp(cp,".def") == 0)
		{
			*cp = 0;
			sprintf(packet,"%d:%s\n",
				strlen(cur->d_name),cur->d_name);
			writePkt(sock);
		}
		cur = readdir(dirp);
	}
	sprintf(packet,"-100:\n");
	writePkt(sock);
	closedir(dirp);
	msqlTrace(TRACE_OUT,"msqlListTables()");
	return;
}






/****************************************************************************
** 	_msqlListFields
**
**	Purpose	: Send a list of table fields to the client
**	Args	: Client socket.  Table and database names
**	Returns	: 
**	Notes	: 
*/

void msqlServerListFields(sock,table,db)
	int	sock;
	char	*table,
		*db;
{
 	field_t	*curField;
	char	buf[50],
		buf2[50];
	cache_t	*cacheEntry;
	mindex_t	*curIndex;

	msqlTrace(TRACE_IN,"msqlListFields()");
	msqlDebug(MOD_GENERAL,"Table to list = %s\n",table);
	if((cacheEntry = loadTableDef(table,NULL,db)))
	{
      		curField = cacheEntry->def;
		while(curField)
		{
			sprintf(buf,"%d",curField->length);
			sprintf(buf2,"%d",curField->type);
			sprintf(packet,"%d:%s%d:%s%d:%s%d:%s1:%s1:%s", 
				strlen(table), table,
				strlen(curField->name), curField->name, 
				strlen(buf2), buf2,
				strlen(buf), buf, 
				curField->flags & NOT_NULL_FLAG?"Y":"N", "N");
			writePkt(sock);
			curField = curField->next;
		}

      		curIndex = cacheEntry->indices;
		while(curIndex)
		{
			sprintf(buf2,"%d",IDX_TYPE);
			sprintf(packet,"%d:%s%d:%s%d:%s%d:%s1:%s1:%s", 
				strlen(table), table,
				strlen(curIndex->name), curIndex->name, 
				strlen(buf2), buf2,
				1, "0", "N",
				curIndex->unique?"Y":"N");
			writePkt(sock);
			curIndex = curIndex->next;
		}
	}
	sprintf(packet,"-100:\n");
	writePkt(sock);
	msqlTrace(TRACE_OUT,"msqlListFields()");
}



/****************************************************************************
** 	_msqlListIndex
**
**	Purpose	: List the fields in an index
**	Args	: Client socket.  Table and database names
**	Returns	: 
**	Notes	: 
*/

void msqlServerListIndex(sock,index,table,db)
	int	sock;
	char	*index,
		*table,
		*db;
{
 	mindex_t	*curIndex;
	cache_t	*cacheEntry;
	char	*idxType;
	field_t	*curField;
	int	count,
		offset;

	msqlTrace(TRACE_IN,"msqlListIndex()");
	msqlDebug(MOD_GENERAL,"Index to list = %s\n",index);
	if((cacheEntry = loadTableDef(table,NULL,db)))
	{
      		curIndex = cacheEntry->indices;
		while(curIndex)
		{
			if (strcmp(curIndex->name,index) == 0)
			{
				break;
			}
			curIndex = curIndex->next;
		}
		if (curIndex)
		{
			switch(curIndex->idxType)
			{
				case IDX_AVL:
					idxType = "avl";
					break;
				default:
					idxType = "???";
					break;
			}
			sprintf(packet,"%d:%s\n",strlen(idxType),idxType);
			writePkt(sock);
			if (curIndex)
			{
				count = 0;
				while(count < curIndex->fieldCount)
				{
					offset = curIndex->fields[count];
					curField = cacheEntry->def;
					while(offset)
					{
						curField = curField->next;
						offset--;
					}
					sprintf(packet,"%d:%s\n",
						strlen(curField->name),
						curField->name);
					count ++;
					writePkt(sock);
				}
			}
		}
	}
	sprintf(packet,"-100:\n");
	writePkt(sock);
	msqlTrace(TRACE_OUT,"msqlListFields()");
}



/****************************************************************************
** 	_msqlBackendInit
**
**	Purpose	: Any db backend startup code - called per query
**	Args	: None
**	Returns	: Nothing
**	Notes	: 
*/

void msqlBackendInit()
{
	(void)bzero(tableCache,sizeof(tableCache));
}





/****************************************************************************
** 	_msqlBackendClean
**
**	Purpose	: Any db cleanup code - called after query processed
**	Args	: None
**	Returns	: Nothing
**	Notes	: 
*/

void msqlBackendClean()
{
	selectWildcard = 0;
	selectDistinct = 0;
}






static void freeCacheEntry(entry)
	cache_t	*entry;
{
	msqlTrace(TRACE_IN,"freeCacheEntry()");
	freeTableDef(entry->def);
	entry->def = NULL;
	*(entry->db) = 0;
	*(entry->table) = 0;
	entry->age = 0;
	if (entry->dataMap != (caddr_t) NULL)
	{
		munmap(entry->dataMap,entry->size);
		entry->dataMap = NULL;
		entry->size = 0;
	}
	if (entry->overflowMap != (caddr_t) NULL)
	{
		munmap(entry->overflowMap,entry->overflowSize);
		entry->overflowMap = NULL;
		entry->overflowSize = 0;
	}
	close(entry->dataFD);
	close(entry->overflowFD);
	closeIndices(entry);
	if (entry->row.buf)
	{
		free(entry->row.buf);
		entry->row.buf = NULL;
	}
	msqlTrace(TRACE_OUT,"freeCacheEntry()");
}



void msqlDropCache()
{
	int	index = 0;

	while(index < CACHE_SIZE)
	{
		if (tableCache[index].def)
		{
			freeCacheEntry(tableCache + index);
		}
		index++;
	}
}



/****************************************************************************
** 	_checkNullFields
**
**	Purpose	: Ensure that fields flagged "not null" have a value
**	Args	: table row
**	Returns	: -1 on error
**	Notes	:
*/

static int checkNullFields(cacheEntry,row)
	cache_t	*cacheEntry;
	row_t	*row;
{
	REG 	field_t	*curField;
	REG 	int	offset;
	u_char	*data;

	msqlTrace(TRACE_IN,"checkNullFields()");
	data = row->data;
	offset = 0;
	curField = cacheEntry->def;
	while(curField)
	{
		if (!*(data + offset) && (curField->flags & NOT_NULL_FLAG))
		{
			sprintf(errMsg,BAD_NULL_ERROR, curField->name);
			msqlDebug(MOD_ERR,"Field \"%s\" cannot be null\n",
				curField->name);
			msqlTrace(TRACE_OUT,"checkNullFields()");
			return(-1);
		}
		offset += curField->length + 1;
		curField = curField->next;
	}
	msqlTrace(TRACE_OUT,"checkNullFields()");
	return(0);
}



void msqlQualifyFields(table,fields)
	char	*table;
	field_t	*fields;
{
	field_t	*curField;

	msqlTrace(TRACE_IN,"msqlQualifyField()");
	curField = fields;
	while(curField)
	{
		if(*(curField->table) == 0)
		{
			(void)strcpy(curField->table,table);
		}
		curField=curField->next;
	}
	msqlTrace(TRACE_OUT,"msqlQualifyField()");
}


void msqlQualifyConds(table,conds)
	char	*table;
	cond_t	*conds;
{
	cond_t	*curCond;

	msqlTrace(TRACE_IN,"msqlQualifyConds()");
	curCond = conds;
	while(curCond)
	{
		if (curCond->subCond)
		{
			msqlQualifyConds(table,curCond->subCond);
			curCond = curCond->next;
			continue;
		}
		if(*(curCond->table) == 0)
		{
			(void)strcpy(curCond->table,table);
		}
		curCond=curCond->next;
	}
	msqlTrace(TRACE_OUT,"msqlQualifyConds()");
}



void msqlQualifyOrder(table,order)
	char	*table;
	order_t	*order;
{
	order_t	*curOrder;

	msqlTrace(TRACE_IN,"msqlQualifyOrder()");
	curOrder = order;
	while(curOrder)
	{
		if(*(curOrder->table) == 0)
		{
			(void)strcpy(curOrder->table,table);
		}
		curOrder=curOrder->next;
	}
	msqlTrace(TRACE_OUT,"msqlQualifyOrder()");
}





/****************************************************************************
** 	_msqlSetupFields
**
**	Purpose	: Determine the byte offset into a row of the desired fields
**	Args	: Empty field list (field location) array,
**		  List of desired fields 
**	Returns	: -1 on error
**	Notes	: The field list array holds the byte offsets for the
**		  fields.  ie. array element 0 will hold the byte offset
**		  of the first desired field etc.
*/

int msqlSetupFields(cacheEntry,flist, fields)
	cache_t	*cacheEntry;
	int	*flist;
	field_t	*fields;
{
	REG 	field_t	*curField,
			*fieldDef;
	int	numFields,
		*curFL,
		curOffset,
		count,
		res;

	msqlTrace(TRACE_IN,"msqlSetupFields()");
	numFields = 0;
	curField = fields;
	curFL = flist;
	while(curField)
	{
		numFields++;
		if (numFields < MAX_FIELDS)
			*curFL++ = -1;
		curField=curField->next;
	}
	if (numFields > MAX_FIELDS)
	{
		sprintf(errMsg,FIELD_COUNT_ERROR);
		msqlDebug(MOD_ERR,"Too many fileds in query\n");
		msqlTrace(TRACE_OUT,"msqlSetupFields()");
		return(-1);
	}
	*curFL = -1;

	curField = fields;
	curFL = flist;
	while(curField)
	{
	    if (*curField->name == '_')
	    {
		if ((res = checkSysVar(cacheEntry,curField)) < 0)
		{
			if (res == -2)
				return(-1);
		
			sprintf(errMsg, SYSVAR_ERROR, curField->name);
			msqlDebug(MOD_ERR,SYSVAR_ERROR, curField->name);
			return(-1);
		}
	    	curField->sysvar = 1;
		curField = curField->next;
		continue;
	    }
	    curField->sysvar = 0;
	    fieldDef = cacheEntry->def;
	    curOffset = count = 0;
	    while(fieldDef)
	    {
		if(strcmp(curField->name,fieldDef->name) == 0 &&
		   strcmp(curField->table,fieldDef->table) == 0)
		{
			curField->type = fieldDef->type;
			curField->length = fieldDef->length;
			curField->flags = fieldDef->flags;
			curField->entry = fieldDef->entry;
			curField->offset = curOffset;
			curField->fieldID = count;
			curField->overflow = NO_POS;
			*curFL = curOffset;
			if (!curField->value)
				break;
			if (!curField->value->nullVal)
			{
				switch(curField->type)
				{
					case INT_TYPE:
						if(curField->value->type
							!= INT_TYPE)
						{
							sprintf(errMsg,
								TYPE_ERROR,
								curField->name);
							msqlDebug(MOD_ERR,
								TYPE_ERROR,
								curField->name);
							return(-1);
						}
						break;
		
					case CHAR_TYPE:
					case TEXT_TYPE:
						if(curField->value->type
							!= CHAR_TYPE &&
						   curField->value->type
							!= TEXT_TYPE )
						{
							sprintf(errMsg,
								TYPE_ERROR,
								curField->name);
							msqlDebug(MOD_ERR,
								TYPE_ERROR,
								curField->name);
							return(-1);
						}
						break;

					case REAL_TYPE:
						if(curField->value->type
							== INT_TYPE)
						{
						  curField->value->val.realVal
						  = curField->value->val.intVal;
                        			  curField->value->type = 
							REAL_TYPE;
						}
						if(curField->value->type
							!= REAL_TYPE)
						{
							sprintf(errMsg,
								TYPE_ERROR,
								curField->name);
							msqlDebug(MOD_ERR,
								TYPE_ERROR,
								curField->name);
							return(-1);
						}
						break;
				}
			}
			break;
		}
		curOffset += fieldDef->dataLength+1; /* +1 for null indicator */
		fieldDef = fieldDef->next;
		count++;
	    }
	    if(!fieldDef)  /* Bad entry */
	    {
		if (curField->table)
		{
		    sprintf(errMsg,BAD_FIELD_ERROR,
				curField->table,curField->name);
		    msqlDebug(MOD_ERR,"Unknown field \"%s.%s\"\n",
				curField->table,curField->name);
		    msqlTrace(TRACE_OUT,"msqlSetupFields()");
		    return(-1);
		}
		else
		{
		    sprintf(errMsg,BAD_FIELD_2_ERROR,curField->name);
		    msqlDebug(MOD_ERR,"Unknown field \"%s\"\n",curField->name);
		    msqlTrace(TRACE_OUT,"msqlSetupFields()");
		    return(-1);
		}
	    }
	    curFL++;
	    curField = curField->next;
	}
	msqlTrace(TRACE_OUT,"msqlSetupFields()");
	return(0);
}





/****************************************************************************
** 	_msqlSetupConds
**
**	Purpose	: Determine the byte offset into a row for conditional
**		  data.
**	Args	: Condition list (field location) array,
**		  List of fileds used in conditionals
**	Returns	: -1 on error
**	Notes	: As per msqlSetupFields.
*/

int msqlSetupConds(cacheEntry,conds)
	cache_t	*cacheEntry;
	cond_t	*conds;
{
	REG 	cond_t	*curCond;
	REG 	field_t	*fieldDef;
	int	numConds,
		*curFL,
		curOffset,
		fieldID,
		res;

	msqlTrace(TRACE_IN,"msqlSetupConds()");
	numConds = 0;
	curCond = conds;
	curFL = NULL;
	if (conds)
	{
		if (conds->clist)
		{
			/*(void)bzero(conds->clist,MAX_FIELDS * sizeof(int));*/
			curFL = conds->clist;
		}
	}
	while(curCond)
	{
		if (curCond->subCond)
		{
			curCond = curCond->next;
			continue;
		}
		numConds++;
		if (numConds < MAX_FIELDS)
			*curFL++ = -1;
		curCond=curCond->next;
	}
	if (numConds > MAX_FIELDS)
	{
		sprintf(errMsg,COND_COUNT_ERROR);
		msqlDebug(MOD_ERR,"Too many fields in condition\n");
		msqlTrace(TRACE_OUT,"msqlSetupConds()");
		return(-1);
	}
	if (curFL)
		*curFL = -1;
	
	curCond = conds;
	curFL = conds->clist;
	while(curCond)
	{
		if (curCond->subCond)
		{
			if (msqlSetupConds(cacheEntry, curCond->subCond)<0)
			{
				return(-1);	
			}
			curCond = curCond->next;
			continue;
		}
            	if (*curCond->name == '_')
            	{
			if ((res = checkSysVarCond(curCond)) < 0)
			{
				if (res == -2)
					return(-1);
                        	sprintf(errMsg, SYSVAR_ERROR, curCond->name);
                        	msqlDebug(MOD_ERR,SYSVAR_ERROR, curCond->name);
				return(-1);
			}
			curCond->sysvar = 1;
			curCond->fieldID = -2;
			curCond = curCond->next;
			continue;
		}
		curCond->sysvar = 0;
		fieldDef = cacheEntry->def;
		curOffset = 0;
		fieldID = 0;
		while(fieldDef)
		{
			if(strcmp(curCond->name,fieldDef->name) == 0 &&
			   strcmp(curCond->table,fieldDef->table) == 0)
			{
				curCond->type = fieldDef->type;
				curCond->length = fieldDef->length;
				*curFL = curOffset;
				break;
			}
			curOffset += fieldDef->dataLength+1; /* +1 for null */
			fieldDef = fieldDef->next;
			fieldID ++;
		}
		if (!fieldDef)
		{
			sprintf(errMsg,BAD_FIELD_2_ERROR, curCond->name);
			msqlDebug(MOD_ERR,
				"Unknown field in where clause \"%s\"\n",
				curCond->name);
			msqlTrace(TRACE_OUT,"msqlSetupConds()");
			return(-1);
		}
		curFL++;
		curCond->fieldID = fieldID;
		curCond->type = fieldDef->type;
		curCond = curCond->next;
	}
	msqlTrace(TRACE_OUT,"msqlSetupConds()");
	return(0);
}



/****************************************************************************
** 	_msqlSetupOrder
**
**	Purpose	: Determine the byte offset into a row for order
**		  data.
**	Args	: Order list (field location) array,
**		  List of fileds used in order
**	Returns	: -1 on error
**	Notes	: As per msqlSetupFields.
*/

int msqlSetupOrder(cacheEntry,olist, order)
	cache_t	*cacheEntry;
	int	*olist;
	order_t	*order;
{
	REG 	order_t	*curOrder;
	REG 	field_t	*fieldDef;
	int	numOrder,
		*curFL,
		curOffset;

	msqlTrace(TRACE_IN,"msqlSetupOrder()");
	numOrder = 0;
	curOrder = order;
	curFL = olist;
	while(curOrder)
	{
		numOrder++;
		if (numOrder < MAX_FIELDS)
			*curFL++ = -1;
		curOrder=curOrder->next;
	}
	if (numOrder > MAX_FIELDS)
	{
		sprintf(errMsg,ORDER_COUNT_ERROR);
		msqlDebug(MOD_ERR,"Too many fields in order specification\n");
		msqlTrace(TRACE_OUT,"msqlSetupOrder()");
		return(-1);
	}
	*curFL = -1;
	
	curOrder = order;
	curFL = olist;
	while(curOrder)
	{
		fieldDef = cacheEntry->def;
		curOffset = 0;
		while(fieldDef)
		{
			if(strcmp(curOrder->name,fieldDef->name) == 0 &&
			   strcmp(curOrder->table,fieldDef->table) == 0)
			{
				curOrder->type = fieldDef->type;
				curOrder->length = fieldDef->length;
				curOrder->entry = fieldDef->entry;
				*curFL = curOffset;
				break;
			}
			curOffset += fieldDef->dataLength+1; /* +1 for null */
			fieldDef = fieldDef->next;
		}
		if (!fieldDef)
		{
			sprintf(errMsg,
				cacheEntry->result==0?
				BAD_FIELD_2_ERROR:BAD_ORD_FIELD_2_ERROR,
				curOrder->name);
			msqlDebug(MOD_ERR,
				"Unknown field in order clause \"%s\"\n",
				curOrder->name);
			msqlTrace(TRACE_OUT,"msqlSetupOrder()");
			return(-1);
		}
		curFL++;
		curOrder = curOrder->next;
	}
	msqlTrace(TRACE_OUT,"msqlSetupOrder()");
	return(0);
}






/****************************************************************************
** 	_dupFieldList
**
**	Purpose	: 
**	Args	: 
**	Returns	: 
**	Notes	: 
*/

field_t *dupFieldList(cacheEntry)
	cache_t	*cacheEntry;
{
	field_t	*fieldDef,
		*prevField,
		*newField,
		*head;


	/*
	** Scan the field list
	*/

	msqlTrace(TRACE_IN,"dupFieldList()");
	prevField = head = NULL;
	fieldDef = cacheEntry->def;
	while(fieldDef)
	{
		newField = (field_t *)malloc(sizeof(field_t));
		strcpy(newField->name,fieldDef->name);
		strcpy(newField->table,fieldDef->table);
		if (!prevField)
		{
			head = newField;
		}
		else
			prevField->next = newField;
		newField->next = NULL;
		prevField = newField;
		fieldDef = fieldDef->next;
	}
	msqlTrace(TRACE_OUT,"dupFieldList()");
	return(head);
}



static void freeFieldList(head)
        field_t *head;
{
        field_t *cur,
                *prev;

        cur = head;
        while(cur)
        {
                prev = cur;
                cur = cur->next;
		if (prev->value)
			msqlFreeValue(prev->value);
		prev->next = NULL;
		free(prev);
        }
}



/****************************************************************************
** 	_expandWildCards
**
**	Purpose	: Handle "*" in a select clause
**	Args	: 
**	Returns	: 
**	Notes	: This just drops the entire table into the field list
**		  when it finds a "*"
*/

field_t *expandFieldWildCards(cacheEntry,fields)
	cache_t	*cacheEntry;
	field_t	*fields;
{
	field_t	*curField,
		*fieldDef,
		*prevField,
		*newField,
		*tmpField,
		*head;


	/*
	** Scan the field list
	*/

	msqlTrace(TRACE_IN,"expandWildcard()");
	head = curField = fields;
	prevField = NULL;
	while(curField)
	{
		if (strcmp(curField->name,"*") == 0)
		{
			/*
			** Setup a new entry for each field
			*/
			fieldDef = cacheEntry->def;
			while(fieldDef)
			{
				newField = (field_t *)malloc(sizeof(field_t));
				strcpy(newField->name,fieldDef->name);
				strcpy(newField->table,fieldDef->table);
				newField->overflow = NO_POS;
				if (!prevField)
				{
					head = newField;
				}
				else
					prevField->next = newField;
				newField->next = curField->next;
				prevField = newField;
				fieldDef = fieldDef->next;
			}

			/*
			** Blow away the wildcard entry
			*/
			if (curField->type == CHAR_TYPE || 
			    curField->type == TEXT_TYPE)
				safeFree(curField->value->val.charVal);
			tmpField = curField;
			curField = curField->next;
			safeFree(tmpField);
		}
		else
		{
			prevField = curField;
			curField = curField->next;
		}
	}
	msqlTrace(TRACE_OUT,"expandWildcard()");
	return(head);
}



void expandTableFields(table)
	char    *table;
{
	cache_t *cacheEntry;
	extern char *curDB;
	char	tableName[NAME_LEN];

	msqlTrace(TRACE_IN,"expandTableFields()");
	strcpy(tableName,table);
	if((cacheEntry = loadTableDef(tableName,NULL,curDB)))
	{
		fieldHead = expandFieldWildCards(cacheEntry,fieldHead);
	}
	msqlTrace(TRACE_OUT,"expandTableFields()");
}





int msqlInit(db)
	char	*db;
{
	char	path[MAXPATHLEN];
	struct	stat buf;

	msqlTrace(TRACE_IN,"msqlInit()");
	(void)sprintf(path,"%s/msqldb/%s",msqlHomeDir,db);
	if (stat(path,&buf) < 0)
	{
		sprintf(errMsg,BAD_DB_ERROR,db);
		msqlDebug(MOD_ERR,"Unknown database \"%s\"\n",db);
		msqlTrace(TRACE_OUT,"msqlInit()");
		return(-1);
	}
	msqlTrace(TRACE_OUT,"msqlInit()");
	return(0);
}



int msqlServerCreateTable(table,fields,db)
	char	*table;
	field_t	*fields;
	char	*db;
{
	char	defPath[255],
		datPath[255],
		oflPath[255],
		idxPath[255];
	field_t	*curField;
	int	fd,
		fieldCount,
		foundKey;
	sblk_t 	sblock;
	u_int	freeList;

	msqlTrace(TRACE_IN,"msqlCreate()");

	/*
	** Write the catalog entry
	*/
	(void)sprintf(defPath,"%s/msqldb/%s/%s.def",msqlHomeDir,db,table);
#ifdef OS2
	fd = _sopen(defPath,O_RDONLY,SH_DENYNO, S_IREAD | S_IWRITE);
#else
	fd = open(defPath,O_RDONLY,0);
#endif
	if (fd >= 0)
	{
		(void)close(fd);
		sprintf(errMsg,TABLE_EXISTS_ERROR,table);
		msqlDebug(MOD_ERR,"Table \"%s\" exists\n",table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
#ifdef OS2
        fd = _sopen(defPath, O_WRONLY | O_CREAT | O_BINARY, SH_DENYNO, 
		S_IREAD | S_IWRITE);
#else
	fd = open(defPath,O_WRONLY | O_CREAT, 0600);
#endif
	if (fd < 0)
	{
		sprintf(errMsg,TABLE_FAIL_ERROR,table);
		msqlDebug(MOD_ERR,"Can't create table \"%s\"\n",table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	
	/*
	** Ensure that there aren't too many fields
	*/
	curField = fields;
	fieldCount = foundKey = 0;
	while(curField)
	{
		(void)strcpy(curField->table,table);
		fieldCount++;
		curField = curField->next;
	}
	if (fieldCount > MAX_FIELDS)
	{
		close(fd);
		unlink(defPath);
		sprintf(errMsg,TABLE_WIDTH_ERROR,MAX_FIELDS);
		msqlDebug(MOD_ERR,"Too many fields in table (%d Max)\n",
			MAX_FIELDS);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}

	/*
	** Dump the field definition to the table def file
	*/
	curField = fields;
	while(curField)
	{
		if(write(fd,curField,sizeof(field_t)) <0)
		{
			(void)close(fd);
			unlink(defPath);
			sprintf(errMsg,CATALOG_WRITE_ERROR);
			msqlDebug(MOD_ERR,"Error writing catalog\n");
			msqlTrace(TRACE_OUT,"msqlCreate()");
			return(-1);
		}
		curField = curField->next;
	}
	(void)close(fd);


	/*
	** Create an empty table
	*/
	
	(void)sprintf(datPath,"%s/msqldb/%s/%s.dat",msqlHomeDir,db,table);
	(void)unlink(datPath);
#ifdef OS2
        fd = _sopen(datPath,O_CREAT | O_WRONLY | O_BINARY, SH_DENYNO, 
		S_IREAD | S_IWRITE);
#else
	fd = open(datPath,O_CREAT | O_WRONLY, 0600);
#endif
	if (fd < 0)
	{
		unlink(datPath);
		unlink(defPath);
		sprintf(errMsg,DATA_FILE_ERROR,table);
		msqlDebug(MOD_ERR,"Error creating table file for \"%s\"\n",
			table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	bzero(&sblock, sizeof(sblock));
	sblock.version = DB_VERSION;
	sblock.numRows = sblock.activeRows = 0;
	sblock.freeList = NO_POS;
	if (write(fd,&sblock,SBLK_SIZE) < SBLK_SIZE)
	{
		close(fd);
		unlink(datPath);
		unlink(defPath);
		sprintf(errMsg,DATA_FILE_ERROR,table);
		msqlDebug(MOD_ERR,"Error creating table file for \"%s\"\n",
			table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	close(fd);


	/*
	** Create an empty index def file
	*/
	
	(void)sprintf(idxPath,"%s/msqldb/%s/%s.idx",msqlHomeDir,db,table);
	(void)unlink(idxPath);
#ifdef OS2
        fd = _sopen(idxPath,O_CREAT | O_WRONLY | O_BINARY, SH_DENYNO, 
		S_IREAD | S_IWRITE);
#else
	fd = open(idxPath,O_CREAT | O_WRONLY, 0600);
#endif
	if (fd < 0)
	{
		unlink(datPath);
		unlink(defPath);
		sprintf(errMsg,DATA_FILE_ERROR,table);
		msqlDebug(MOD_ERR,"Error creating table file for \"%s\"\n",
			table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	close(fd);

	/*
	** Create an empty overflow file
	*/
	(void)sprintf(oflPath,"%s/msqldb/%s/%s.ofl",msqlHomeDir,db,table);
	(void)unlink(oflPath);
#ifdef OS2
        fd = _sopen(oflPath,O_CREAT | O_WRONLY | O_BINARY, SH_DENYNO, 
		S_IREAD | S_IWRITE);
#else
	fd = open(oflPath,O_CREAT | O_WRONLY, 0600);
#endif
	if (fd < 0)
	{
		unlink(datPath);
		unlink(defPath);
		unlink(idxPath);
		sprintf(errMsg,DATA_FILE_ERROR,table);
		msqlDebug(MOD_ERR,"Error creating table file for \"%s\"\n",
			table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	freeList = NO_POS;
	if (write(fd,&freeList,sizeof(u_int)) < sizeof(u_int))
	{
		close(fd);
		unlink(datPath);
		unlink(defPath);
		unlink(idxPath);
		sprintf(errMsg,DATA_FILE_ERROR,table);
		msqlDebug(MOD_ERR,"Error creating table file for \"%s\"\n",
			table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	close(fd);

	
	sprintf(packet,"1:\n");
	writePkt(outSock);
	msqlTrace(TRACE_OUT,"msqlCreate()");
	return(0);
}




static int msqlServerBulkIndex(index,db)
	mindex_t	*index;
	char		*db;
{
	cache_t	*entry;
	mindex_t *idx;
	u_int	rowNum;
	row_t	row;
	int	fullFlist[MAX_FIELDS],
		res;

	entry = loadTableDef(index->table,NULL,db);
	if (!entry)
	{
		return(-1);
	}
	idx = entry->indices;
	while(idx)
	{
		if (strcmp(idx->name, index->name) == 0)
			break;
		idx = idx->next;
	}
	msqlSetupFields(entry,fullFlist, entry->def);
	rowNum = 0;
	while(rowNum < entry->sblk->numRows)
	{
		if (rowRead(entry,&row,rowNum) < 0)
		{
			return(-1);
		}
		if (row.header->active == 0)
		{
			rowNum++;
			continue;
		}
		extractValues(entry,&row,entry->def,fullFlist);
		if (checkIndexNullFields(entry,&row,idx) < 0)
        	{
                	return(-1);
        	}
        	res = checkIndex(entry, entry->def, idx);
        	if (res < 0)
        	{
                	msqlTrace(TRACE_OUT,"msqlServerBulkIndex()");
                	return(-1);
        	}
        	if (res == 0)
        	{
                	sprintf(errMsg,KEY_UNIQ_ERROR);
                	msqlDebug(MOD_ERR,
				"Non unique value for unique index\n");
                	msqlTrace(TRACE_OUT,"msqlServerBulkIndex()");
                	return(-1);
        	}
		insertIndex(entry, entry->def, rowNum, idx);
		rowNum++;
	}
	sprintf(packet,"1:\n");
	writePkt(outSock);
        msqlTrace(TRACE_OUT,"msqlServerBulkIndex()");
}

int msqlServerCreateIndex(index,fields,db)
	mindex_t	*index;
	field_t	*fields;
	char	*db;
{
	char	defPath[255],
		idxPath[255];
	field_t	*curField,
		*indexField;
	int	fd,
		fieldLoc,
		fieldCount,
		length,
		existingData;
	cache_t	*entry;
	mindex_t	tmp;

	msqlTrace(TRACE_IN,"msqlCreateIndex()");

	/*
	** Ensure the index name doesn't clash with a field
	*/
	if ((entry = loadTableDef(index->table,NULL,db)) == NULL)
	{
		msqlTrace(TRACE_OUT,"msqlCreateIndex()");
		return(-1);
	}

	existingData = (entry->sblk->activeRows > 0)?1:0;
	length = 0;
	curField = entry->def;
	while(curField)
	{
		if (strcmp(curField->name,index->name) == 0)
		{
			sprintf(errMsg,"Bad index name");
			msqlTrace(TRACE_OUT,"msqlCreateIndex()");
			return(-1);
		}
		curField = curField->next;
	}

	/*
	** Can't clash with another index either
	*/
	(void)sprintf(defPath,"%s/msqldb/%s/%s.idx",msqlHomeDir,db,
		index->table);

	(void)sprintf(idxPath,"%s/msqldb/%s/%s.idx-%s",msqlHomeDir,db,
		index->table, index->name);

#ifdef OS2
        fd = _sopen(defPath,O_RDWR | O_CREAT | O_BINARY, SH_DENYNO, 
		S_IREAD | S_IWRITE);
#else
	fd = open(defPath,O_RDWR | O_CREAT,0600);
#endif
	if (fd < 0)
	{
		(void)close(fd);
		sprintf(errMsg,"Can't open index definition file");
		msqlTrace(TRACE_OUT,"msqlCreateIndex()");
		return(-1);
	}

	while( read(fd, &tmp, sizeof(tmp)) == sizeof(tmp))
	{
		if (strcmp(tmp.name, index->name) == 0)
		{
			sprintf(errMsg,"Bad index name");
			msqlTrace(TRACE_OUT,"msqlCreateIndex()");
			close(fd);
			return(-1);
		}
	}
	
	/*
	** OK, setup the struct and add it to the index def file
	*/
	indexField = fields;
	fieldCount = 0;
	while(indexField)
	{
		fieldLoc = 0;
		curField = entry->def;
		while(curField)
		{
			if (strcmp(curField->name, indexField->name)==0)
			{
				break;
			}
			curField = curField->next;
			fieldLoc++;
		}
		if (!curField)
		{
			sprintf(errMsg,"Unknown field '%s'",indexField->name);
			msqlTrace(TRACE_OUT,"msqlCreateIndex()");
			close(fd);
			return(-1);
		}
		if (curField->type == TEXT_TYPE)
		{
			sprintf(errMsg,"Can't index on a TEXT field!");
			msqlTrace(TRACE_OUT,"msqlCreateIndex()");
			close(fd);
			return(-1);
		}
		index->fields[fieldCount] = fieldLoc;
		fieldCount++;
		length += curField->length;
		if(fieldCount > MAX_INDEX_WIDTH - 1)
		{
			sprintf(errMsg,"Too many fields in index");
			msqlTrace(TRACE_OUT,"msqlCreateIndex()");
			close(fd);
			return(-1);
		}
		indexField = indexField->next;
	}
	index->fields[fieldCount] = -1;
	index->length = length;
	index->fieldCount = fieldCount;
	write(fd, index, sizeof(mindex_t));
	close(fd);

#ifndef BERK_DB
	if (fieldCount == 1)
	{
		if (curField->type == CHAR_TYPE)
			avlCreate(idxPath, 0600, length, AVL_CHAR, AVL_DUP);
		else if (curField->type == INT_TYPE)
			avlCreate(idxPath, 0600, length, AVL_INT, AVL_DUP);
		else if (curField->type == REAL_TYPE)
			avlCreate(idxPath, 0600, length, AVL_REAL, AVL_DUP);
		else
			avlCreate(idxPath, 0600, length, AVL_BYTE, AVL_DUP);
	}
	else
	{
		avlCreate(idxPath, 0600, length, AVL_BYTE, AVL_DUP);
	}
#endif

        /*
        ** Invalidate the cache entry so that we reload it
        */

	freeTableDef(entry->def);
	entry->def = NULL;
	*(entry->db) = 0;
	*(entry->table) = 0;
	entry->age = 0;
	if (entry->dataMap != (caddr_t) NULL)
	{
		munmap(entry->dataMap,entry->size);
		entry->dataMap = NULL;
		entry->size = 0;
	}
	if (entry->overflowMap != (caddr_t) NULL)
	{
		munmap(entry->overflowMap,entry->overflowSize);
		entry->overflowMap = NULL;
		entry->overflowSize = 0;
	}
	close(entry->dataFD);
	close(entry->overflowFD);
	closeIndices(entry);

	/*
	** If there's data in the table then prime the index
	*/
	if (existingData)
	{
		return(msqlServerBulkIndex(index,db));
	}
	sprintf(packet,"1:\n");
	writePkt(outSock);
	msqlTrace(TRACE_OUT,"msqlCreateIndex()");
	return(0);
}




int msqlServerDropSequence(table,db)
	char	*table;
	char	*db;
{
	cache_t	*entry;

	msqlTrace(TRACE_IN,"msqlDropSequence()");

	/*
	** See if there is a sequence on this table
	*/
	if ((entry = loadTableDef(table,NULL,db)) == NULL)
	{
		msqlTrace(TRACE_OUT,"msqlCreateIndex()");
		return(-1);
	}
	if (entry->sblk->sequence.step == 0)
	{
		sprintf(errMsg,"Table '%s' does not have a sequence",table);
		msqlTrace(TRACE_OUT,"msqlCreateSequence()");
		return(-1);
	}

	entry->sblk->sequence.step = 0;
	entry->sblk->sequence.value = 0;
	sprintf(packet,"1:\n");
	writePkt(outSock);
	msqlTrace(TRACE_OUT,"msqlCreateSequence()");
	return(0);
}



int msqlServerCreateSequence(table,step,val,db)
	char	*table;
	int	step,
		val;
	char	*db;
{
	cache_t	*entry;

	msqlTrace(TRACE_IN,"msqlCreateSequence()");

	/*
	** See if there is a sequence on this table already
	*/
	if ((entry = loadTableDef(table,NULL,db)) == NULL)
	{
		msqlTrace(TRACE_OUT,"msqlCreateIndex()");
		return(-1);
	}
	if (entry->sblk->sequence.step != 0)
	{
		sprintf(errMsg,"Table '%s' already has a sequence",table);
		msqlTrace(TRACE_OUT,"msqlCreateSequence()");
		return(-1);
	}

	entry->sblk->sequence.step = step;
	entry->sblk->sequence.value = val;
	sprintf(packet,"1:\n");
	writePkt(outSock);
	msqlTrace(TRACE_OUT,"msqlCreateSequence()");
	return(0);
}




int msqlServerDropTable(table,db)
	char	*table,
		*db;
{
	char	path[MAXPATHLEN],
		defPath[MAXPATHLEN];
	FILE	*fp;
	int	fd;
	mindex_t	tmp;
	REG 	cache_t *entry;
	REG 	int	count;

	msqlTrace(TRACE_IN,"msqlDrop()");

	/* 
	** Invalidate the cache entry so that we don't use it again 
	*/

	count = 0;
	while(count < CACHE_SIZE)
	{
		entry = tableCache + count;
		if((strcmp(entry->db,db)==0) &&
		   (strcmp(entry->table,table)==0 ||
		    strcmp(entry->cname,table)==0))
		{
			msqlDebug(MOD_CACHE,"Clearing cache entry %d (%s:%s)\n",
				count,db,table);
			freeTableDef(entry->def);
			entry->def = NULL;
			*(entry->db) = 0;
			*(entry->table) = 0;
			entry->age = 0;
			if (entry->dataMap != (caddr_t) NULL)
			{
				munmap(entry->dataMap,entry->size);
				entry->dataMap = NULL;
				entry->size = 0;
			}
			if (entry->overflowMap != (caddr_t) NULL)
			{
				munmap(entry->overflowMap,entry->overflowSize);
				entry->overflowMap = NULL;
				entry->overflowSize = 0;
			}
			close(entry->dataFD);
			close(entry->overflowFD);
			closeIndices(entry);
		}
		count++;
	}

	/*
	** Now blow away the table files
	*/
	(void)sprintf(path,"%s/msqldb/%s/%s.def",msqlHomeDir,db,table);
#ifdef OS2
	fp = fopen(path,"rb");
#else
	fp = fopen(path,"r");
#endif
	if (!fp)
	{
		sprintf(errMsg,BAD_TABLE_ERROR,table);
		msqlDebug(MOD_ERR,"Unknown table \"%s\"\n",table);
		msqlTrace(TRACE_OUT,"msqlDrop()");
		return(-1);
	}
	(void)fclose(fp);
	unlink(path);
	(void)sprintf(path,"%s/msqldb/%s/%s.dat",msqlHomeDir,db,table);
	unlink(path);
	(void)sprintf(path,"%s/msqldb/%s/%s.ofl",msqlHomeDir,db,table);
	unlink(path);

	/*
	** Take care of the index files.
	*/
	(void)sprintf(defPath,"%s/msqldb/%s/%s.idx",msqlHomeDir,db,table);
#ifdef OS2
        fd = _sopen(defPath,O_RDONLY | O_BINARY, SH_DENYNO, S_IREAD | S_IWRITE);
#else
	fd = open(defPath,O_RDONLY,0);
#endif
	if (fd >= 0)
	{
		while(read(fd,&tmp,sizeof(tmp)) == sizeof(tmp))
		{
			(void)sprintf(path,"%s/msqldb/%s/%s.idx-%s",
				msqlHomeDir,db,table,tmp.name);
			unlink(path);
		}
		close(fd);
	}
	unlink(defPath);

	sprintf(packet,"1:\n");
	writePkt(outSock);
	msqlTrace(TRACE_OUT,"msqlDrop()");
	return(0);
}



int msqlServerDropIndex(index,db)
	mindex_t	*index;
	char	*db;
{
	char	defPath[255],
		tmpPath[255];
	int	in,
		out,
		found;
	mindex_t	tmp;
	cache_t	*entry;


	msqlTrace(TRACE_IN,"msqlDropIndex()");

	/*
	** Open the definition file and a file to copy it into
	*/
	if ((entry = loadTableDef(index->table,NULL,db)) == NULL)
	{
		msqlTrace(TRACE_OUT,"msqlDropIndex()");
		return(-1);
	}

	(void)sprintf(defPath,"%s/msqldb/%s/%s.idx",msqlHomeDir,db,
		index->table);
#ifdef OS2
        in = _sopen(defPath,O_RDONLY | O_BINARY, SH_DENYNO, S_IREAD | S_IWRITE);
#else
	in = open(defPath,O_RDONLY,0600);
#endif
	if (in < 0)
	{
		sprintf(errMsg,"No indices defined for '%s'",index->table);
		msqlTrace(TRACE_OUT,"msqlDropIndex()");
		return(-1);
	}
	(void)sprintf(tmpPath,"%s/msqldb/%s/%s.idx-tmp",msqlHomeDir,db,
		index->table);
#ifdef OS2
        out = _sopen(tmpPath,O_RDWR | O_CREAT | O_BINARY, SH_DENYNO, 
		S_IREAD | S_IWRITE);
#else
	out = open(tmpPath,O_RDWR | O_CREAT,0600);
#endif
	if (out < 0)
	{
		(void)close(in);
		sprintf(errMsg,"Can't create index copy file");
		msqlTrace(TRACE_OUT,"msqlDropIndex()");
		return(-1);
	}

	/*
	** Copy the definitions over, skipping the dropped index
	*/
	found = 0;
	while( read(in, &tmp, sizeof(tmp)) == sizeof(tmp))
	{
		if (strcmp(tmp.name, index->name) != 0)
		{
			write(out, &tmp, sizeof(tmp));
		}
		else
		{
			found = 1;
		}
	}
	
	/*
	** Did we find it?  
	*/
	close(in);
	close(out);
	if (!found)
	{
		sprintf(errMsg,"Unknown index '%s' for '%s'",index->name, 
			index->table);
		unlink(tmpPath);
		msqlTrace(TRACE_OUT,"msqlDropIndex()");
		return(-1);
	}

	/*
	** Yup.  Do the rest of the job.
	*/
	unlink(defPath);
	rename(tmpPath,defPath);
	sprintf(tmpPath,"%s/msqldb/%s/%s.idx-%s",msqlHomeDir,db,
                index->table, index->name);
	unlink(tmpPath);

        /*
        ** Invalidate the cache entry so that we reload it
        */

	freeTableDef(entry->def);
	entry->def = NULL;
	*(entry->db) = 0;
	*(entry->table) = 0;
	entry->age = 0;
	if (entry->dataMap != (caddr_t) NULL)
	{
		munmap(entry->dataMap,entry->size);
		entry->dataMap = NULL;
		entry->size = 0;
	}
	if (entry->overflowMap != (caddr_t) NULL)
	{
		munmap(entry->overflowMap,entry->overflowSize);
		entry->overflowMap = NULL;
		entry->overflowSize = 0;
	}
	close(entry->dataFD);
	close(entry->overflowFD);
	closeIndices(entry);


	sprintf(packet,"1:\n");
	writePkt(outSock);
	msqlTrace(TRACE_OUT,"msqlDropIndex()");
	return(0);
}



int msqlServerDelete(table,conds,db)
	char	*table;
	cond_t	*conds;
	char	*db;
{
	int	flist[MAX_FIELDS],
		rowLen,
		res,
		count;
	u_int	rowNum;
	row_t	row;
	field_t	 *fields;
	cache_t	*cacheEntry;
	cand_t	*candidate;


	msqlTrace(TRACE_IN,"msqlServerDelete()");
	if((cacheEntry = loadTableDef(table,NULL,db)) == NULL)
	{
		msqlTrace(TRACE_OUT,"msqlServerDelete()");
		return(-1);
	}

	/*
	** Find the offsets of the given condition
	*/
	msqlQualifyConds(table,conds);
	if (msqlSetupConds(cacheEntry,conds) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerDelete()");
		return(-1);
	}

	if (initTable(cacheEntry,FULL_REMAP) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerDelete()");
		return(-1);
	}


	fields = dupFieldList(cacheEntry);
	if (msqlSetupFields(cacheEntry,flist,fields) < 0)
	{
		freeFieldList(fields);
		msqlTrace(TRACE_OUT,"msqlServerDelete()");
		return(-1);
	}
	rowLen = cacheEntry->rowLen;
	count = 0;
	candidate = msqlSetupCandidate(cacheEntry, conds, fields, KEEP_IDENT);
	if (!candidate)
	{
		freeFieldList(fields);
		msqlTrace(TRACE_OUT,"msqlServerDelete");
		return(-1);
	}
	rowNum = getCandidate(cacheEntry,candidate);
	while(rowNum != NO_POS)
	{
		if (rowRead(cacheEntry,&row,rowNum) < 0)
		{
			break;
		}
		if (!row.header->active)
		{
			rowNum++;
			continue;
		}
		res = matchRow(cacheEntry,&row,conds);
		if (res < 0)
		{
			freeFieldList(fields);
			freeCandidate(candidate);
			msqlTrace(TRACE_OUT,"msqlServerDelete()");
			return(res);
		}
		if (res == 1)
		{
			count++;
			cacheEntry->sblk->activeRows--;
			res = deleteRow(cacheEntry,rowNum);
			if(res < 0)
			{
				msqlTrace(TRACE_OUT,"msqlServerDelete()");
				freeCandidate(candidate);
				freeFieldList(fields);
				return(res);
			}
			extractValues(cacheEntry, &row, fields, flist);
			deleteIndices(cacheEntry,fields,rowNum);
			pushBlankPos(cacheEntry,db,table,rowNum);

			/*
			** Have to reset this.  If it's an index based
			** lookup then the delete may have shuffled a 
			** a dup up the chain.  A getnext would skip the
			** correct entry.
			*/
			resetCandidate(candidate);
		}
		rowNum = getCandidate(cacheEntry,candidate);
	}
	sprintf(packet,"%d:\n",count);
	writePkt(outSock);
	msqlTrace(TRACE_OUT,"msqlServerDelete()");
	freeCandidate(candidate);
	freeFieldList(fields);
	return(0);
}




int msqlServerInsert(table,fields,db)
	char	*table;
	field_t	*fields;
	char	*db;
{
	int	flist[MAX_FIELDS],
		rowLen,
		res;
	u_int	rowNum;
	row_t	*row;
	REG 	field_t	*curField,
		*curField2;
	cache_t	*cacheEntry;


	msqlTrace(TRACE_IN,"msqlServerInsert()");
	if((cacheEntry = loadTableDef(table,NULL,db)) == NULL)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}

	/*
	** Find the offsets of the given fields
	*/
	msqlQualifyFields(table,fields);
	if (msqlSetupFields(cacheEntry,flist,fields) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}

	/*
	** Ensure that no field is listed more than once and that each
	** field was given a value.
	*/
	curField = fields;
	while(curField)
	{
		if (!curField->value)
		{
			sprintf(errMsg, NO_VALUE_ERROR, curField->name);
			msqlDebug(MOD_ERR,
				"No value specified for field '%s'",
				curField->name);
			msqlTrace(TRACE_OUT,"msqlServerInsert()");
			return(-1);
		}
		curField2 = curField;
		while(curField2)
		{
			if (curField2 == curField)
			{
				curField2 = curField2->next;
				continue;
			}
			if (strcmp(curField->name,curField2->name) == 0 &&
			    strcmp(curField->table,curField2->table) == 0)
			{
				sprintf(errMsg,NON_UNIQ_ERROR, curField->name);
				msqlDebug(MOD_ERR,"Field '%s' not unique",
					curField->name);
				msqlTrace(TRACE_OUT,"msqlServerInsert()");
				return(-1);
			}
			curField2 = curField2->next;
		}
		curField = curField->next;
	}

	/*
	** Create a blank row
	*/
	rowLen = cacheEntry->rowLen;
	row = &(cacheEntry->row);

	/*
	** Find a place to put this row
	*/

	rowNum = popBlankPos(cacheEntry,db,table);

	if (initTable(cacheEntry,KEY_REMAP) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}

	/*
	** Check for a unique primary key if we have one.
	*/
	res = checkIndices(cacheEntry, fields);
	if (res < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	if (res == 0)
	{
		sprintf(errMsg,KEY_UNIQ_ERROR);
		msqlDebug(MOD_ERR,"Non unique value for unique index\n");
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	/*
	** Fill in the given fields and dump it to the table file
	*/

	(void)bzero(row->data,rowLen);
	if (fillRow(cacheEntry,row,fields,flist) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	if (checkNullFields(cacheEntry,row) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	if (checkIndicesNullFields(cacheEntry,row) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	insertIndices(cacheEntry, fields, rowNum);
	if(rowWrite(cacheEntry,NULL,rowNum) < 0)
	{
		sprintf(errMsg,"Error on data write");
		msqlDebug(MOD_ERR,"Error on data write\n");
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	cacheEntry->sblk->activeRows++;
	
	sprintf(packet,"1:\n");
	writePkt(outSock);
	msqlTrace(TRACE_OUT,"msqlServerInsert()");
	return(0);
}



int msqlServerUpdate(table,fields,conds,db)
	char	*table;
	field_t	*fields;
	cond_t	*conds;
	char	*db;
{
	int	flist[MAX_FIELDS],
		fullFlist[MAX_FIELDS],
		rowLen,
		res,
		count;
	u_int	rowNum;
	row_t	row;
	cache_t	*cacheEntry;
	cand_t	*candidate;
	field_t	*curField;
	


	msqlTrace(TRACE_IN,"msqlServerUpdate()");
	if((cacheEntry = loadTableDef(table,NULL,db)) == NULL)
	{
		msqlTrace(TRACE_OUT,"msqlServerUpdate()");
		return(-1);
	}

	/*
	** Find the offsets of the given fields and condition
	*/
	msqlQualifyFields(table,fields);
	msqlQualifyConds(table,conds);
	if (msqlSetupFields(cacheEntry,flist,fields) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerUpdate()");
		return(-1);
	}
	if (msqlSetupConds(cacheEntry,conds) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerUpdate()");
		return(-1);
	}

	rowLen = cacheEntry->rowLen;

	if (initTable(cacheEntry,FULL_REMAP) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerUpdate()");
		return(-1);
	}

	count = 0;
	candidate = msqlSetupCandidate(cacheEntry, conds, fields, KEEP_IDENT);
	if (!candidate)
		return(-1);
	rowNum = getCandidate(cacheEntry, candidate);
	while(rowNum != NO_POS)
	{
		rowRead(cacheEntry,&row,rowNum);
		if (!row.header->active)
		{
			rowNum = getCandidate(cacheEntry, candidate);
			continue;
		}
		res = matchRow(cacheEntry,&row,conds);
		if (res < 0)
		{
			freeCandidate(candidate);
			return(-1);
		}
		if (res == 1)
		{
			if (cacheEntry->indices)
			{
				msqlSetupFields(cacheEntry,fullFlist,
					cacheEntry->def);
				extractValues(cacheEntry,&row,
					cacheEntry->def,fullFlist);
			}
			bcopy(row.buf, cacheEntry->row.buf,
				(cacheEntry->rowLen + HEADER_SIZE));
			if (updateValues(cacheEntry,&row,fields,flist) < 0)
			{
				msqlTrace(TRACE_OUT,"msqlServerUpdate()");
				freeCandidate(candidate);
				return(-1);
			}
			if (checkNullFields(cacheEntry,&row) < 0)
			{
				bcopy(cacheEntry->row.buf, row.buf,
					(cacheEntry->rowLen + HEADER_SIZE));
				msqlTrace(TRACE_OUT,"msqlServerUpdate()");
				freeCandidate(candidate);
				return(-1);
			}
			if (checkIndicesNullFields(cacheEntry,&row) < 0)
			{
				bcopy(cacheEntry->row.buf, row.buf,
					(cacheEntry->rowLen + HEADER_SIZE));
				msqlTrace(TRACE_OUT,"msqlServerUpdate()");
				freeCandidate(candidate);
				return(-1);
			}
        		res = checkIndices(cacheEntry, fields);
        		if (res < 0)
        		{
				bcopy(cacheEntry->row.buf, row.buf,
					(cacheEntry->rowLen + HEADER_SIZE));
                		msqlTrace(TRACE_OUT,"msqlServerUpdate()");
                		return(-1);
        		}
        		if (res == 0)
        		{
				bcopy(cacheEntry->row.buf, row.buf,
					(cacheEntry->rowLen + HEADER_SIZE));
                		sprintf(errMsg,KEY_UNIQ_ERROR);
                		msqlDebug(MOD_ERR,
					"Non unique value for unique index\n");
                		msqlTrace(TRACE_OUT,"msqlServerUpdate()");
                		return(-1);
        		}

			if(rowWrite(cacheEntry,&row,rowNum) < 0)
			{
				sprintf(errMsg,WRITE_ERROR);
				msqlDebug(MOD_ERR,"Error on data write\n");
				msqlTrace(TRACE_OUT,"msqlServerUpdate()");
				freeCandidate(candidate);
				return(-1);
			}
			count++;
			if (cacheEntry->indices)
			{
				deleteIndices(cacheEntry, cacheEntry->def, 
					rowNum);
				if (initTable(cacheEntry,FULL_REMAP) < 0)
				{
					msqlTrace(TRACE_OUT,
						"msqlServerUpdate()");
					return(-1);
				}
				extractValues(cacheEntry,&row,
					cacheEntry->def,fullFlist);
				insertIndices(cacheEntry, cacheEntry->def, 
					rowNum);
				curField = cacheEntry->def;
				while (curField)
				{
					if (curField->value)
					{
						msqlFreeValue(curField->value);
						curField->value = NULL;
					}
					curField = curField->next;
				}                               
			}
		}
		rowNum = getCandidate(cacheEntry, candidate);
	}
	sprintf(packet,"%d:\n",count);
	writePkt(outSock);
	freeCandidate(candidate);
	msqlTrace(TRACE_OUT,"msqlServerUpdate()");
	return(0);
}




void formatPacket(packet,fields)
	char	*packet;
	field_t	*fields;
{
	char	outBuf[100],
		bufLen[10];
	u_char	*outData;
	field_t	*curField;

	msqlTrace(TRACE_IN,"formatPacket()");
	*packet = 0;
	curField = fields;
	while(curField)
	{
		if (!curField->value->nullVal)
		{
			switch(curField->type)
			{
				case INT_TYPE:
					sprintf(outBuf,"%d",
					    curField->value->val.intVal);
					outData = (u_char *)outBuf;
					break;
				case UINT_TYPE:
					sprintf(outBuf,"%u",
					    curField->value->val.intVal);
					outData = (u_char *)outBuf;
					break;
				case CHAR_TYPE:
				case TEXT_TYPE:
					outData = curField->value->val.charVal;
					break;
				case REAL_TYPE:
					/* Analogy Start */
					sprintf(outBuf,"%.16g",
					    curField->value->val.realVal);
					/* Analogy End */
					outData = (u_char *)outBuf;
					break;
			}
			sprintf(bufLen,"%d:",strlen((char *)outData));
			strcat(packet,bufLen);
			strcat(packet,(char *)outData);
		}
		else
		{
			strcat(packet,"-2:");
		}
		curField = curField->next;
	}
	strcat(packet,"\n");
	msqlTrace(TRACE_OUT,"formatPacket()");
}




row_t *dupRow(entry,row, new)
	cache_t	*entry;
	row_t 	*row,
		*new;
{
	if (!new)
	{
		new = (row_t *)fastMalloc(sizeof(row_t));
		new->buf = (u_char *)fastMalloc(entry->rowLen+2 + HEADER_SIZE);
	}
	(void)bcopy(row->buf,new->buf,entry->rowLen + HEADER_SIZE);
	new->header = (hdr_t *)new->buf;
	new->data = new->buf + HEADER_SIZE;
	return(new);
}


void freeRow(row)
	row_t	*row;
{
	safeFree(row->buf);
	safeFree(row);
}





void msqlServerCreateDB(sock,db)
	int	sock;
	char	*db;
{
	char	path[MAXPATHLEN];
	DIR	*dirp;

	/*
	** See if the directory exists
	*/

	(void)sprintf(path,"%s/msqldb/%s", msqlHomeDir, db);
	dirp = opendir(path);
	if (dirp)
	{
		closedir(dirp);
		sprintf(packet,"-1:Error creating database : %s exists!\n",db);
                writePkt(sock);
		return;
	}

	/*
	** Create the directory
	*/
#ifdef OS2
	if (mkdir(path) < 0)
#else
	if (mkdir(path,0700) < 0)
#endif
	{
		sprintf(packet,"-1:Error creating database\n");
		writePkt(sock);
		return;
	}
	sprintf(packet,"-100:\n");
	writePkt(sock);
}


void msqlServerDropDB(sock,db)
	int	sock;
	char	*db;
{
	char	path[MAXPATHLEN],
		filePath[255];
	DIR	*dirp;
#ifdef HAVE_DIRENT
	struct	dirent *cur;
#else
	struct	direct *cur;
#endif
	int	index;
	cache_t	*entry;

	/*
	** See if the directory exists
	*/

	(void)sprintf(path,"%s/msqldb/%s", msqlHomeDir, db);
	dirp = opendir(path);
	if (!dirp)
	{
		sprintf(packet,"-1:Error dropping database : %s doesn't exist\n"
			, db);
		writePkt(sock);
		return;
	}

	/*
	** Blow away any files but dodge '.' and '..'
	*/

	cur = readdir(dirp);
	cur = readdir(dirp);
	cur = readdir(dirp);
	while(cur)
	{
		sprintf(filePath,"%s/%s",path,cur->d_name);
		unlink(filePath);
		cur = readdir(dirp);
	}
		
	if (rmdir(path) < 0)
	{
		sprintf(packet,"-1:Error dropping database\n");
		writePkt(sock);
		closedir(dirp);
		return;
	}
	closedir(dirp);

	/*
	** Invalidate any cache entries that are for this DB
	*/
	index = 0;
	while(index < CACHE_SIZE)
	{
		entry = tableCache + index++;
		if (strcmp(entry->db,db) == 0)
		{
			freeTableDef(entry->def);
			entry->def = NULL;
			*(entry->db) = 0;
			*(entry->table) = 0;
			entry->age = 0;
			close(entry->dataFD);
			close(entry->overflowFD);
			if (entry->dataMap != (caddr_t) NULL)
			{
				munmap(entry->dataMap,entry->size);
				entry->dataMap = NULL;
				entry->size = 0;
			}
			if (entry->overflowMap != (caddr_t) NULL)
			{
				munmap(entry->overflowMap,entry->overflowSize);
				entry->overflowMap = NULL;
				entry->overflowSize = 0;
			}
		}
	}
	sprintf(packet,"-100:\n");
	writePkt(sock);
}
