/*	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>
#include <errno.h>

#ifdef WIN32
#  include <io.h>
#  include <winsock.h>
#endif


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

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

#include <common/portability.h>

#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>

#if defined(OS2) || defined(WIN32)
#  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

#ifndef HAVE_SNPRINTF
extern	int snprintf();
#endif



cache_t	tableCache[CACHE_SIZE];

extern	int	outSock;
extern	char	*packet;

int	selectWildcard = 0,
	selectDistinct = 0;

char	errMsg[MAX_ERR_MSG];
char	*msqlHomeDir;

cache_t *loadTableDef();
int msqlDropDB();




void initBackend()
{
	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_H
	struct	dirent *cur;
#else
	struct	direct *cur;
#endif

	msqlTrace(TRACE_IN,"msqlListDBs()");
	(void)snprintf(path, MAXPATHLEN, "%s/msqldb",msqlHomeDir);
	dirp = opendir(path);
	if (!dirp)
	{
		snprintf(errMsg,MAX_ERR_MSG,BAD_DIR_ERROR,path, 
			(char*)strerror(errno));
		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;
		}
		snprintf(packet,PKT_LEN, "%d:%s\n",(int)strlen(cur->d_name),
			cur->d_name);
		writePkt(sock);
		cur = readdir(dirp);
	}
	strcpy(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_H
	struct	dirent *cur;
#else
	struct	direct *cur;
#endif

	msqlTrace(TRACE_IN,"msqlListTables()");
	(void)snprintf(path,MAXPATHLEN,"%s/msqldb/%s",msqlHomeDir,db);
	dirp = opendir(path);
	if (!dirp)
	{
		snprintf(errMsg,MAX_ERR_MSG,BAD_DIR_ERROR,path, 
			(char*)strerror(errno));
		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;
			snprintf(packet,PKT_LEN, "%d:%s\n",
				(int)strlen(cur->d_name),cur->d_name);
			writePkt(sock);
		}
		cur = readdir(dirp);
	}
	strcpy(packet,"-100:\n");
	writePkt(sock);
	closedir(dirp);
	msqlTrace(TRACE_OUT,"msqlListTables()");
	return;
}




/****************************************************************************
** 	_msqlServerSequenceInfo
**
**	Purpose	: Send sequence information for a table
**	Args	: 
**	Returns	: 
**	Notes	: 
*/

void msqlServerSequenceInfo(sock,table,db)
	int	sock;
	char	*table,
		*db;
{
	cache_t	*cacheEntry;

	msqlTrace(TRACE_IN,"msqlServerSequenceInfo()");
	if((cacheEntry = loadTableDef(table,NULL,db)))
	{
		if (cacheEntry->sblk->sequence.step)
		{
			snprintf(packet,PKT_LEN,"1:%d:%d",
				cacheEntry->sblk->sequence.step,
				cacheEntry->sblk->sequence.value);
		}
		else
		{
			snprintf(packet,PKT_LEN, 
				"-1:No sequence defined on '%s'\n", table);
		}
	}
	else
	{
		snprintf(packet,PKT_LEN,"-1:%s\n",errMsg);
	}
	writePkt(sock);
	msqlTrace(TRACE_OUT,"msqlServerSequenceInfo()");
}



/****************************************************************************
** 	_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)
		{
			snprintf(buf,sizeof(buf),"%d",curField->length);
			snprintf(buf2,sizeof(buf2),"%d",curField->type);
			snprintf(packet,PKT_LEN,"%d:%s%d:%s%d:%s%d:%s1:%s1:%s", 
				(int)strlen(table), table,
				(int)strlen(curField->name), curField->name, 
				(int)strlen(buf2), buf2,
				(int)strlen(buf), buf, 
				curField->flags & NOT_NULL_FLAG?"Y":"N", "N");
			writePkt(sock);
			curField = curField->next;
		}

      		curIndex = cacheEntry->indices;
		while(curIndex)
		{
			snprintf(buf2,sizeof(buf2),"%d",IDX_TYPE);
			snprintf(packet,PKT_LEN,"%d:%s%d:%s%d:%s%d:%s1:%s1:%s", 
				(int)strlen(table), table,
				(int)strlen(curIndex->name), curIndex->name, 
				(int)strlen(buf2), buf2,
				1, "0", "N",
				curIndex->unique?"Y":"N");
			writePkt(sock);
			curIndex = curIndex->next;
		}
		strcpy(packet,"-100:\n");
		writePkt(sock);
	}
	else
	{
		snprintf(packet,PKT_LEN,"-1:Unknown table '%s'\n", table);
		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 (*(curIndex->name) != *index)
			{
				curIndex = curIndex->next;
				continue;
			}
			if (strcmp(curIndex->name,index) != 0)
			{
				curIndex = curIndex->next;
				continue;
			}
			break;
		}
		if (curIndex)
		{
			switch(curIndex->idxType)
			{
				case IDX_AVL:
					idxType = "avl";
					break;
				default:
					idxType = "???";
					break;
			}
			snprintf(packet,PKT_LEN,"%d:%s\n",(int)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--;
					}
					snprintf(packet,PKT_LEN,"%d:%s\n",
						(int)strlen(curField->name),
						curField->name);
					count ++;
					writePkt(sock);
				}
			}
		}
	}
	strcpy(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()
{
	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)
	{
		MSYNC(entry->dataMap,entry->size,0);
		munmap(entry->dataMap,entry->size);
		entry->dataMap = NULL;
		entry->size = 0;
	}
	if (entry->overflowMap != (caddr_t) NULL)
	{
		MSYNC(entry->overflowMap,entry->overflowSize,0);
		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, flist)
	cache_t	*cacheEntry;
	row_t	*row;
	int	*flist;
{
	REG 	field_t	*curField;
	int	*curfl;
	u_char	*data;

	msqlTrace(TRACE_IN,"checkNullFields()");
	data = row->data;
	curfl = flist;
	curField = cacheEntry->def;
	while(curField)
	{
		if (!*(data + *curfl) && (curField->flags & NOT_NULL_FLAG))
		{
			snprintf(errMsg,MAX_ERR_MSG,BAD_NULL_ERROR, 
				curField->name);
			msqlDebug(MOD_ERR,"Field \"%s\" cannot be null\n",
				curField->name);
			msqlTrace(TRACE_OUT,"checkNullFields()");
			return(-1);
		}
		curfl++;
		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;
	char	*cp;

	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)
	{
		snprintf(errMsg,MAX_ERR_MSG,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 == '_' && ! cacheEntry->result)
	    {
		if ((res = checkSysVar(cacheEntry,curField)) < 0)
		{
			if (res == -2)
				return(-1);
		
			snprintf(errMsg, MAX_ERR_MSG, 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( *(curField->name) != *(fieldDef->name) ||
		    *(curField->table) != *(fieldDef->table))
		{
			curOffset+=fieldDef->dataLength+1;/* +1 for null flag */
			fieldDef = fieldDef->next;
			count++;
			continue;
		}
		if(strcmp(curField->name,fieldDef->name) != 0 ||
		   strcmp(curField->table,fieldDef->table) != 0)
		{
			curOffset+=fieldDef->dataLength+1;/* +1 for null flag */
			fieldDef = fieldDef->next;
			count++;
			continue;
		}

		curField->type = fieldDef->type;
		curField->length = fieldDef->length;
		curField->dataLength = fieldDef->dataLength;
		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:
				case UINT_TYPE:
					if(curField->value->type != INT_TYPE&&
					   curField->value->type != UINT_TYPE)
					{
						snprintf(errMsg, MAX_ERR_MSG,
							TYPE_ERROR,
							curField->name);
						msqlDebug(MOD_ERR, TYPE_ERROR,
							curField->name);
						return(-1);
					}
					break;
	
				case MONEY_TYPE:
					if(curField->value->type != MONEY_TYPE&&
					   curField->value->type != INT_TYPE&&
					   curField->value->type != UINT_TYPE&&
					   curField->value->type != REAL_TYPE)
					{
						snprintf(errMsg, MAX_ERR_MSG,
							TYPE_ERROR,
							curField->name);
						msqlDebug(MOD_ERR, TYPE_ERROR,
							curField->name);
						return(-1);
					}
					curField->value->val.intVal =
						scanMoney(curField->value);
					curField->value->type = MONEY_TYPE;
					break;

				case DATE_TYPE:
					cp = NULL;
					if(curField->value->type == CHAR_TYPE)
					{
						cp = (char *)
						   curField->value->val.charVal;
						curField->value->val.intVal=
							__msqlScanDate(cp);
						curField->value->type=DATE_TYPE;
					}
					if(curField->value->type != DATE_TYPE)
					{
						snprintf(errMsg, MAX_ERR_MSG,
							TYPE_ERROR,
							curField->name);
						msqlDebug(MOD_ERR, TYPE_ERROR,
							curField->name);
						return(-1);
					}
					if (curField->value->val.intVal == -1)
					{
						snprintf(errMsg, MAX_ERR_MSG,
							DATE_ERROR, cp);
						msqlDebug(MOD_ERR, DATE_ERROR,
							cp);
						free(cp);
						return(-1);
					}
					free(cp);
					break;

				case TIME_TYPE:
					cp = NULL;
					if(curField->value->type == CHAR_TYPE)
					{
						cp = (char *)
						   curField->value->val.charVal;
						curField->value->val.intVal=
							__msqlScanTime(cp);
						curField->value->type=TIME_TYPE;
					}
					if(curField->value->type != TIME_TYPE)
					{
						snprintf(errMsg, MAX_ERR_MSG,
							TYPE_ERROR,
							curField->name);
						msqlDebug(MOD_ERR, TYPE_ERROR,
							curField->name);
						return(-1);
					}
					if (curField->value->val.intVal == -1)
					{
						snprintf(errMsg, MAX_ERR_MSG,
							TIME_ERROR, cp);
						msqlDebug(MOD_ERR, TIME_ERROR,
							cp);
						free(cp);
						return(-1);
					}
					free(cp);
					break;
	
				case CHAR_TYPE:
				case TEXT_TYPE:
					if(curField->value->type != CHAR_TYPE &&
					   curField->value->type != TEXT_TYPE )
					{
						snprintf(errMsg, MAX_ERR_MSG,
							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)
					{
						snprintf(errMsg, MAX_ERR_MSG,
							TYPE_ERROR,
							curField->name);
						msqlDebug(MOD_ERR, TYPE_ERROR,
							curField->name);
						return(-1);
					}
					break;
			}
		}
		break;
	    }
	    if(!fieldDef)  /* Bad entry */
	    {
		if (curField->table)
		{
		    snprintf(errMsg,MAX_ERR_MSG, 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
		{
		    snprintf(errMsg,MAX_ERR_MSG, 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);
}




int msqlSetCondValueType(curCond, value)
	cond_t	*curCond;
	val_t	*value;
{
	int	intVal,
		valType;


	if (value->nullVal)
	{
		return(0);
	}
	if (value->type == IDENT_TYPE)
	{
		return(0);
	}
	switch(curCond->type)
	{
		case DATE_TYPE:
			if (value->type == CHAR_TYPE)
			{
				intVal = __msqlScanDate(value->val.charVal);
				if (intVal == -1)
				{
					snprintf(errMsg,MAX_ERR_MSG,DATE_ERROR, 
						curCond->name);
					return(-1);
				}
				free(value->val.charVal);
				value->val.intVal = intVal;
				value->type = DATE_TYPE;
			}
			break;

		case TIME_TYPE:
			if (value->type == CHAR_TYPE)
			{
				intVal = __msqlScanTime(value->val.charVal);
				if (intVal == -1)
				{
					snprintf(errMsg,MAX_ERR_MSG,TIME_ERROR, 
						curCond->name);
					return(-1);
				}
				free(value->val.charVal);
				value->val.intVal = intVal;
				value->type = TIME_TYPE;
			}
			break;

		case MONEY_TYPE:
			if (value->type == INT_TYPE ||	
			    value->type == UINT_TYPE ||	
			    value->type == REAL_TYPE )
			{
				intVal = scanMoney(value);
				value->val.intVal = intVal;
				value->type = MONEY_TYPE;
			}
			break;

		case REAL_TYPE:
			if (value->type == INT_TYPE || 
			    value->type == UINT_TYPE)
			{
                        	value->val.realVal = value->val.intVal;
				value->type = REAL_TYPE;
			}
			if (value->type == MONEY_TYPE)
			{
                        	value->type = REAL_TYPE;
                        	value->val.realVal = value->val.intVal / 100;
			}
			break;


		case UINT_TYPE:
			if (value->type == INT_TYPE)
			{
                        	value->type = UINT_TYPE;
			}
			if (value->type == MONEY_TYPE)
			{
                        	value->type = UINT_TYPE;
                        	value->val.intVal /= 100;
			}
			break;

		case INT_TYPE:
			if (value->type == UINT_TYPE)
			{
                        	value->type = INT_TYPE;
			}
			if (value->type == MONEY_TYPE)
			{
                        	value->type = INT_TYPE;
                        	value->val.intVal /= 100;
			}
			break;
	}
	valType = value->type;

	if (valType == SYSVAR_TYPE)
	{
		valType = getSysVarType(value->val.identVal->seg2);
		if (valType < 0)
		{
			snprintf(errMsg,MAX_ERR_MSG,SYSVAR_ERROR, 
				value->val.identVal->seg2);
			return(-1);
		}
	}

	if (curCond->type != valType &&
		!( curCond->type == TEXT_TYPE && 
		   valType == CHAR_TYPE))
	{
		snprintf(errMsg,MAX_ERR_MSG,BAD_TYPE_ERROR, curCond->name);
		msqlDebug(MOD_ERR,"Bad type for comparison of '%s'",
                                curCond->name);
		return(-1);
	}
	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)
	{
		snprintf(errMsg,MAX_ERR_MSG,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);
                        	snprintf(errMsg, MAX_ERR_MSG, 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( *(curCond->name) != *(fieldDef->name) ||
			    *(curCond->table) != *(fieldDef->table))
			{
				/* +1 for null flag */
				curOffset += fieldDef->dataLength+1; 
				fieldDef = fieldDef->next;
				fieldID ++;
				continue;
			}
			
			if(strcmp(curCond->name,fieldDef->name) != 0 ||
			   strcmp(curCond->table,fieldDef->table) != 0)
			{
				/* +1 for null flag */
				curOffset += fieldDef->dataLength+1; 
				fieldDef = fieldDef->next;
				fieldID ++;
				continue;
			}

			curCond->type = fieldDef->type;
			curCond->length = fieldDef->length;
			curCond->fieldID = fieldID;
			if (msqlSetCondValueType(curCond, curCond->value) < 0)
				return(-1);
			*curFL = curOffset;
			break;
		}
		if (!fieldDef)
		{
			snprintf(errMsg, MAX_ERR_MSG, 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)
	{
		snprintf(errMsg,MAX_ERR_MSG,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( *(curOrder->name) != *(fieldDef->name) ||
			    *(curOrder->table) != *(fieldDef->table))
			{
				/* +1 for null flag */
				curOffset += fieldDef->dataLength+1; 
				fieldDef = fieldDef->next;
				continue;
			}
		
			if(strcmp(curOrder->name,fieldDef->name) != 0 ||
			   strcmp(curOrder->table,fieldDef->table) != 0)
			{
				/* +1 for null flag */
				curOffset += fieldDef->dataLength+1; 
				fieldDef = fieldDef->next;
				continue;
			}

			curOrder->type = fieldDef->type;
			curOrder->length = fieldDef->length;
			curOrder->entry = fieldDef->entry;
			*curFL = curOffset;
			break;
		}
		if (!fieldDef)
		{
			snprintf(errMsg, MAX_ERR_MSG,
				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)snprintf(path, MAXPATHLEN, "%s/msqldb/%s",msqlHomeDir,db);
	if (stat(path,&buf) < 0)
	{
		snprintf(errMsg, MAX_ERR_MSG, 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],
		outBuf[40960];
	REG char	*cp;
	REG	field_t	*curField;
	REG	int	fieldCount;
	int	fd,
		foundKey;
	sblk_t 	sblock;
	u_int	freeList;
	struct	stat sbuf;


	msqlTrace(TRACE_IN,"msqlCreate()");

	/*
	** Write the catalog entry
	*/
	(void)snprintf(defPath, MAXPATHLEN, "%s/msqldb/%s/%s.def",
		msqlHomeDir,db,table);
	if (stat(defPath, &sbuf) >= 0)
	{
		snprintf(errMsg,MAX_ERR_MSG,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)
	{
		snprintf(errMsg,MAX_ERR_MSG,TABLE_FAIL_ERROR,table);
		msqlDebug(MOD_ERR,"Can't create table \"%s\"\n",table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	
	/*
	** Write out the definition and check to ensure that there 
	** aren't too many fields
	*/
	curField = fields;
	fieldCount = foundKey = 0;
	cp = outBuf;
	while(curField)
	{
		(void)strcpy(curField->table,table);
		fieldCount++;
		write(fd, curField, sizeof(field_t));
		curField = curField->next;
	}
	close(fd);
	if (fieldCount > MAX_FIELDS)
	{
		unlink(defPath);
		snprintf(errMsg,MAX_ERR_MSG,TABLE_WIDTH_ERROR,MAX_FIELDS);
		msqlDebug(MOD_ERR,"Too many fields in table (%d Max)\n",
			MAX_FIELDS);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}

	/*
	** Create an empty table
	*/
	
	(void)snprintf(datPath,MAXPATHLEN, "%s/msqldb/%s/%s.dat",
		msqlHomeDir,db,table);
#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);
		snprintf(errMsg, MAX_ERR_MSG,DATA_FILE_ERROR,table, 
			(char*)strerror(errno));
		msqlDebug(MOD_ERR,"Error creating table file for \"%s\"\n",
			table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	sblock.version = DB_VERSION;
	sblock.numRows = sblock.activeRows = 0;
	sblock.freeList = NO_POS;
	sblock.sequence.step = sblock.sequence.value = 0;
	if (write(fd,&sblock,SBLK_SIZE) < SBLK_SIZE)
	{
		close(fd);
		unlink(datPath);
		unlink(defPath);
		snprintf(errMsg, MAX_ERR_MSG, DATA_FILE_ERROR,table, 
			(char*)strerror(errno));
		msqlDebug(MOD_ERR,"Error creating table file for \"%s\"\n",
			table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	close(fd);


	/*
	** NOTE : The index def file file doesn't need to
	** 	  be created here as it is a 0 length file.  It
	**	  is now created using the open when the table is
	**	  first loaded into the table cache
	** 
	** Create an empty overflow file
	*/
	(void)snprintf(oflPath,MAXPATHLEN,"%s/msqldb/%s/%s.ofl",
		msqlHomeDir,db,table);
#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);
		snprintf(errMsg,MAX_ERR_MSG,DATA_FILE_ERROR,table, 
			(char*)strerror(errno));
		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);
		snprintf(errMsg,MAX_ERR_MSG,DATA_FILE_ERROR,table, 
			(char*)strerror(errno));
		msqlDebug(MOD_ERR,"Error creating table file for \"%s\"\n",
			table);
		msqlTrace(TRACE_OUT,"msqlCreate()");
		return(-1);
	}
	close(fd);

	
	strcpy(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 ( *(idx->name) != *(index->name))
		{
			idx = idx->next;
			continue;
		}
		if (strcmp(idx->name, index->name) != 0)
		{
			idx = idx->next;
			continue;
		}
		break;
	}
	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,fullFlist) < 0)
        	{
			msqlServerDropIndex(index,db,1);
                	return(-1);
        	}
        	res = checkIndex(entry, entry->def, idx, NO_POS);
        	if (res < 0)
        	{
			msqlServerDropIndex(index,db,1);
                	msqlTrace(TRACE_OUT,"msqlServerBulkIndex()");
                	return(-1);
        	}
        	if (res == 0)
        	{
			msqlServerDropIndex(index,db,1);
                	snprintf(errMsg,MAX_ERR_MSG,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++;
	}
	strcpy(packet,"1:\n");
	writePkt(outSock);
        msqlTrace(TRACE_OUT,"msqlServerBulkIndex()");
	return(0);
}

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 ( *(curField->name) != *(index->name))
		{
			curField = curField->next;
			continue;
		}
		if (strcmp(curField->name,index->name) != 0)
		{
			curField = curField->next;
			continue;
		}

		strcpy(errMsg,"Bad index name");
		msqlTrace(TRACE_OUT,"msqlCreateIndex()");
		return(-1);
	}

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

	(void)snprintf(idxPath,MAXPATHLEN,"%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);
		strcpy(errMsg,"Can't open index definition file");
		msqlTrace(TRACE_OUT,"msqlCreateIndex()");
		return(-1);
	}

	while( read(fd, &tmp, sizeof(tmp)) == sizeof(tmp))
	{
		if ( *(tmp.name) != *(index->name))
			continue;
		if (strcmp(tmp.name, index->name) != 0)
			continue;
		
		strcpy(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 ( *(curField->name) != *(indexField->name))
			{
				curField = curField->next;
				fieldLoc++;
				continue;
			}
			if (strcmp(curField->name, indexField->name)!=0)
			{
				curField = curField->next;
				fieldLoc++;
				continue;
			}
			break;
		}
		if (!curField)
		{
			snprintf(errMsg,MAX_ERR_MSG,"Unknown field '%s'",
				indexField->name);
			msqlServerDropIndex(index,db,1);
			msqlTrace(TRACE_OUT,"msqlCreateIndex()");
			close(fd);
			return(-1);
		}
		if (curField->type == TEXT_TYPE)
		{
			strcpy(errMsg,"Can't index on a TEXT field!");
			msqlServerDropIndex(index,db,1);
			msqlTrace(TRACE_OUT,"msqlCreateIndex()");
			close(fd);
			return(-1);
		}
		index->fields[fieldCount] = fieldLoc;
		fieldCount++;
		length += curField->length;
		if(fieldCount > MAX_INDEX_WIDTH - 1)
		{
			strcpy(errMsg,"Too many fields in index");
			msqlServerDropIndex(index,db,1);
			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 ||
			 curField->type == UINT_TYPE ||
			 curField->type == MONEY_TYPE ||
			 curField->type == TIME_TYPE ||
			 curField->type == DATE_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
        */

	invalidateCacheEntry(entry);

	/*
	** If there's data in the table then prime the index
	*/
	if (existingData)
	{
		return(msqlServerBulkIndex(index,db));
	}
	strcpy(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)
	{
		snprintf(errMsg,MAX_ERR_MSG,
			"Table '%s' does not have a sequence",table);
		msqlTrace(TRACE_OUT,"msqlCreateSequence()");
		return(-1);
	}

	entry->sblk->sequence.step = 0;
	entry->sblk->sequence.value = 0;
	strcpy(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)
	{
		snprintf(errMsg,MAX_ERR_MSG,
			"Table '%s' already has a sequence",table);
		msqlTrace(TRACE_OUT,"msqlCreateSequence()");
		return(-1);
	}

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




int msqlServerDropTable(table,db)
	char	*table,
		*db;
{
	char	path[MAXPATHLEN],
		defPath[MAXPATHLEN],
		*cp;
	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 (*entry->db == *db)
		{
			if((strcmp(entry->db,db)==0) &&
		   	(strcmp(entry->table,table)==0 ||
		    	strcmp(entry->cname,table)==0))
			{
				invalidateCacheEntry(entry);
			}
		}
		count++;
	}

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

	*cp = 0;
	strcat(path,".dat");
	unlink(path);
	*cp = 0;
	strcat(path,".ofl");
	unlink(path);

	/*
	** Take care of the index files.
	*/
	*cp = 0;
	strcpy(defPath,path);
	strcat(defPath,".idx");
	*cp = 0;
	strcat(path,".idx-");
	cp = path + strlen(path);
#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))
		{
			strcat(path,tmp.name);
			unlink(path);
			*cp = 0;
		}
		close(fd);
	}
	unlink(defPath);

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



int msqlServerDropIndex(index,db, internal)
	mindex_t	*index;
	char	*db;
	int	internal;
{
	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)snprintf(defPath,MAXPATHLEN,"%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)
	{
		snprintf(errMsg,MAX_ERR_MSG,"No indices defined for '%s'",
			index->table);
		msqlTrace(TRACE_OUT,"msqlDropIndex()");
		return(-1);
	}
	(void)snprintf(tmpPath,MAXPATHLEN,"%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);
		strcpy(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)
	{
		snprintf(errMsg,MAX_ERR_MSG, "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);
	snprintf(tmpPath,MAXPATHLEN,"%s/msqldb/%s/%s.idx-%s",msqlHomeDir,db,
                index->table, index->name);
	unlink(tmpPath);

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

	invalidateCacheEntry(entry);

	if (! internal)
	{
		strcpy(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],
		*curOff,
		rowLen,
		res,
		count;
	u_int	rowNum,
		pos;
	row_t	row;
	field_t	 *fields,
		*curField;
	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 = getCandidate(cacheEntry,candidate);
			continue;
		}
		res = matchRow(cacheEntry,&row,conds);
		if (res < 0)
		{
			freeFieldList(fields);
			freeCandidate(candidate);
			msqlTrace(TRACE_OUT,"msqlServerDelete()");
			return(res);
		}
		if (res == 1)
		{
			/*
			** Blow away the row
			*/
			row.header->active = 0;
			count++;
			cacheEntry->sblk->activeRows--;
			/*
			res = deleteRow(cacheEntry,rowNum);
			if(res < 0)
			{
				msqlTrace(TRACE_OUT,"msqlServerDelete()");
				freeCandidate(candidate);
				freeFieldList(fields);
				return(res);
			}
			*/

			/*
			** Blow away any varChar overflow buffers
			** allocated to this row
			*/
			curField = cacheEntry->def;
			curOff = flist;
			while(curField)
			{
				if (curField->type != TEXT_TYPE)
				{
					curField = curField->next;
					curOff++;
					continue;
				}
				if (* (row.data + *curOff) == 1)
				{
					bcopy(row.data + *curOff + 1 + 
						sizeof(int),&pos,sizeof(u_int));
					deleteVarChar(cacheEntry, pos);
				}
				curField = curField->next;
				curOff++;
			}

			/*
			** Blow away any index entries
			*/
			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, MSQL_DELETE);
		}
		rowNum = getCandidate(cacheEntry,candidate);
	}
	snprintf(packet,PKT_LEN,"%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],
		fullFlist[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);
	}
	if (msqlSetupFields(cacheEntry,fullFlist,cacheEntry->def) < 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)
		{
			snprintf(errMsg,MAX_ERR_MSG,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)
			{
				snprintf(errMsg,MAX_ERR_MSG,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, NO_POS);
	if (res < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	if (res == 0)
	{
		strcpy(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
	*/

	bzero(row->data,rowLen);
	if (fillRow(cacheEntry,row,fields,flist) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	if (checkNullFields(cacheEntry,row,fullFlist) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	if (checkIndicesNullFields(cacheEntry,row,fullFlist) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	insertIndices(cacheEntry, fields, rowNum);
	if(rowWrite(cacheEntry,NULL,rowNum) < 0)
	{
		snprintf(errMsg,MAX_ERR_MSG,WRITE_ERROR, 
			(char*)strerror(errno));
		msqlDebug(MOD_ERR,"Error on data write\n");
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		return(-1);
	}
	cacheEntry->sblk->activeRows++;
	
	strcpy(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,
		updated;
	u_int	rowNum;
	row_t	row;
	cache_t	*cacheEntry;
	cand_t	*candidate;
	


	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 (msqlSetupFields(cacheEntry,fullFlist,cacheEntry->def) < 0)
	{
		msqlTrace(TRACE_OUT,"msqlServerInsert()");
		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)
	{
		if (rowRead(cacheEntry,&row,rowNum) < 0)
			return(-1);
		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)
			{
				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);
			}
			/*
			** Force a remap just in case the overflow file
			** has been extended
			*/
			if (initTable(cacheEntry,FULL_REMAP) < 0)
			{
				msqlTrace(TRACE_OUT,"msqlServerUpdate()");
				freeCandidate(candidate);
				return(-1);
			}
			if (checkNullFields(cacheEntry,&row,fullFlist) < 0)
			{
				bcopy(cacheEntry->row.buf, row.buf,
					(cacheEntry->rowLen + HEADER_SIZE));
				msqlTrace(TRACE_OUT,"msqlServerUpdate()");
				freeCandidate(candidate);
				return(-1);
			}
			if (checkIndicesNullFields(cacheEntry,&row,fullFlist)<0)
			{
				bcopy(cacheEntry->row.buf, row.buf,
					(cacheEntry->rowLen + HEADER_SIZE));
				msqlTrace(TRACE_OUT,"msqlServerUpdate()");
				freeCandidate(candidate);
				return(-1);
			}
        		res = checkIndices(cacheEntry, fields, rowNum);
        		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));
                		strcpy(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)
			{
				snprintf(errMsg,MAX_ERR_MSG,WRITE_ERROR, 
					(char*)strerror(errno));
				msqlDebug(MOD_ERR,"Error on data write\n");
				msqlTrace(TRACE_OUT,"msqlServerUpdate()");
				freeCandidate(candidate);
				return(-1);
			}
			count++;
			if (cacheEntry->indices)
			{
				if (updateIndices(cacheEntry,cacheEntry->def,
					rowNum, &row, fullFlist, &updated,
					candidate->index) < 0)
				{
					freeCandidate(candidate);
					return(-1);
				}
				if (updated)
					resetCandidate(candidate, MSQL_DELETE);
			}
		}
		rowNum = getCandidate(cacheEntry, candidate);
	}
	snprintf(packet,PKT_LEN,"%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],
		realFmt[10],
		*cp;
	u_char	*outData;
	field_t	*curField;

	msqlTrace(TRACE_IN,"formatPacket()");
	*packet = 0;
	cp = packet;
	curField = fields;
	while(curField)
	{
		if (!curField->value->nullVal)
		{
			switch(curField->type)
			{
				case INT_TYPE:
					snprintf(outBuf,sizeof(outBuf),"%d",
					    curField->value->val.intVal);
					outData = (u_char *)outBuf;
					break;
				case UINT_TYPE:
					snprintf(outBuf,sizeof(outBuf),"%u",
					    curField->value->val.intVal);
					outData = (u_char *)outBuf;
					break;
				case DATE_TYPE:
					__msqlPrintDate(outBuf,sizeof(outBuf),
						curField->value->val.intVal);
					outData = (u_char *)outBuf;
					break;
				case TIME_TYPE:
					__msqlPrintTime(outBuf,sizeof(outBuf),
						curField->value->val.intVal);
					outData = (u_char *)outBuf;
					break;
				case MONEY_TYPE:
					printMoney(outBuf,sizeof(outBuf),
						curField->value->val.intVal);
					outData = (u_char *)outBuf;
					break;
				case CHAR_TYPE:
				case TEXT_TYPE:
					outData = curField->value->val.charVal;
					break;
				case REAL_TYPE:
					snprintf(realFmt,sizeof(realFmt),
						"%%.%df",
						curField->value->precision);
					snprintf(outBuf,sizeof(outBuf),
						realFmt,
					    	curField->value->val.realVal);
					outData = (u_char *)outBuf;
					break;
			}


			/* inline sprintf dodge */
			{
				register int loop = 100000,
						start=0,
						v=strlen(outData);
				while(loop) {
					if (v / loop > 0 || start) {
						*cp++ = (v / loop) + '0';
						v = v % loop; 
						start=1;
					}
					loop /= 10;
				}
			}
			*cp++ = ':';
			*cp = 0;
			strcat(cp, (char *)outData);
			cp = cp + strlen(cp);
		}
		else
		{
			strcat(cp,"-2:");
			cp += 3;
		}
		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);
	}
	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)snprintf(path,MAXPATHLEN,"%s/msqldb/%s", msqlHomeDir, db);
	dirp = opendir(path);
	if (dirp)
	{
		closedir(dirp);
		snprintf(packet,PKT_LEN, 
			"-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
	{
		snprintf(packet,PKT_LEN, "-1:Error creating database (%s)\n",
			(char*)strerror(errno));
		writePkt(sock);
		return;
	}
	strcpy(packet,"-100:\n");
	writePkt(sock);
}


void msqlServerCopyDB(sock,fromDB, toDB)
	int	sock;
	char	*fromDB,
		*toDB;
{
	char	fromPath[MAXPATHLEN],
		toPath[MAXPATHLEN],
		fromFile[MAXPATHLEN],
		toFile[MAXPATHLEN];
	DIR	*dirp;
#ifdef HAVE_DIRENT_H
        struct  dirent *cur;
#else
        struct  direct *cur;
#endif

	/*
	** See if the "to" directory exists
	*/

	(void)snprintf(toPath,MAXPATHLEN,"%s/msqldb/%s", msqlHomeDir, toDB);
	dirp = opendir(toPath);
	if (dirp)
	{
		closedir(dirp);
		snprintf(packet,PKT_LEN,
			"-1:Error copying database : %s exists!\n", toDB);
                writePkt(sock);
		return;
	}

	/*
	** See if the "from" directory exists
	*/

	(void)snprintf(fromPath,MAXPATHLEN, "%s/msqldb/%s", msqlHomeDir, 
		fromDB);
	dirp = opendir(fromPath);
	if (!dirp)
	{
		snprintf(packet, PKT_LEN,
			"-1:Error copying database : %s doesn't exist!\n",
			fromDB);
                writePkt(sock);
		return;
	}

	/*
	** Create the directory
	*/
#ifdef OS2
	if (mkdir(toPath) < 0)
#else
	if (mkdir(toPath,0700) < 0)
#endif
	{
		snprintf(packet,PKT_LEN, "-1:Error creating database (%s)\n",
			(char*)strerror(errno));
		writePkt(sock);
		return;
	}

	/*
	** Copy the files - skip '.' and '..'
	*/
	cur = readdir(dirp);
	cur = readdir(dirp);
	cur = readdir(dirp);
	while(cur)
	{
		snprintf(fromFile,MAXPATHLEN,"%s/%s",fromPath,cur->d_name);
		snprintf(toFile,MAXPATHLEN,"%s/%s",toPath,cur->d_name);
		if (copyFile(fromFile, toFile) < 0)
		{
			snprintf(packet,PKT_LEN,"-1%s\n",errMsg);
			writePkt(sock);
			msqlDropDB(-1,toDB);
			return;
		}
		cur = readdir(dirp);
	}

	strcpy(packet,"-100:\n");
	writePkt(sock);
}

void msqlServerMoveDB(sock,fromDB, toDB)
	int	sock;
	char	*fromDB,
		*toDB;
{
	char	fromPath[MAXPATHLEN],
		toPath[MAXPATHLEN];
	DIR	*dirp;

	/*
	** See if the "to" directory exists
	*/

	(void)snprintf(toPath,MAXPATHLEN,"%s/msqldb/%s", msqlHomeDir, toDB);
	dirp = opendir(toPath);
	if (dirp)
	{
		closedir(dirp);
		snprintf(packet,PKT_LEN,
			"-1:Error moving database : %s exists!\n",toDB);
                writePkt(sock);
		return;
	}

	/*
	** See if the "from" directory exists
	*/

	(void)snprintf(fromPath,MAXPATHLEN,"%s/msqldb/%s", msqlHomeDir, 
		fromDB);
	dirp = opendir(fromPath);
	if (!dirp)
	{
		snprintf(packet,PKT_LEN, 
			"-1:Error moving database : %s doesn't exist!\n",
			fromDB);
                writePkt(sock);
		return;
	}
	closedir(dirp);

	/*
	** Move the directory
	*/
	if (rename(fromPath, toPath) < 0)
	{
		snprintf(packet,PKT_LEN,"-1:Error moving database (%s)\n",
			(char*)strerror(errno));
		writePkt(sock);
		return;
	}
	invalidateCachedDatabase(fromDB);
	strcpy(packet,"-100:\n");
	writePkt(sock);
}



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

	/*
	** See if the directory exists
	*/

	(void)snprintf(path,MAXPATHLEN,"%s/msqldb/%s", msqlHomeDir, db);
	dirp = opendir(path);
	if (!dirp)
	{
		if (sock < 0)
			return;
		snprintf(packet,PKT_LEN, 
			"-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)
	{
		snprintf(filePath,MAXPATHLEN,"%s/%s",path,cur->d_name);
		unlink(filePath);
		cur = readdir(dirp);
	}
		
	if (rmdir(path) < 0)
	{
		if (sock < 0)
			return;
		snprintf(packet,PKT_LEN,"-1:Error dropping database\n");
		writePkt(sock);
		closedir(dirp);
		return;
	}
	closedir(dirp);

	invalidateCachedDatabase(db);
	if (sock < 0)
		return;
	strcpy(packet,"-100:\n");
	writePkt(sock);
}
