/*************************************************************************
*
*       schema.c              6/10/93
*
*       This file is about SQLGetSchema for WinWord ODBC
*
*       8/23/95 - changed to return proper values for null qualifiers
*                       and for SQLGetSchema(5)
*                 moved SQL data type lookup table into code and out
*                 of resource stringf
*
*************************************************************************/

#include "wbodbc.h"

/*************************************************************************
*
*       wdGetTables -- to get the tables
*
*************************************************************************/
SWORD wdGetTables(ODBCHNDL *hConnect, LPSTR qualifier, LPSTR owner, SWORD num,  SWORD synFlag)
{
	RETCODE retcode;
		
	switch(hConnect->synchronize_flag)
		{
		case 0: 
			break;
		case 21:
		case 31:
		case 41: 
			goto syn1;
		case 22:
		case 32:
		case 42: 
			goto syn2;
		case 23:
		case 33:
		case 43: 
			goto syn3;
		default:
			return(SQL_SynchronizeError);
		}
		
	if (SQLAllocStmt(hConnect->hdbc, &(hConnect->hSynStmt)) != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, NULL);
		return(0);
		}
	
syn1:
	while ((retcode = SQLTables(hConnect->hSynStmt,
		(qualifier)?((*qualifier)?qualifier:NULL):NULL,
		(SWORD) ((qualifier)?((*qualifier)? SQL_NTS:0):0),
		(owner)?((*owner)?owner:NULL):NULL,
		(SWORD) ((owner)?((*owner)? SQL_NTS:0):0),
		NULL, 0, NULL, 0)) == SQL_STILL_EXECUTING) 
		{
		if(synFlag)
			{
			hConnect->synchronize_flag = (num+1)*10+1;
			return(SQL_StillExecuting);
			}
		if (wdPeekMessage()) 
			{
			SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
			return(0);
			}
		}
		
	hConnect->synchronize_flag = 0;
	
	if (retcode != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
		SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
		return(0);
		}
	
	hConnect->lpSynPtr = NULL;
	for (hConnect->synNum = 0;; hConnect->synNum++) 
		{
syn2:
		while((retcode = SQLFetch(hConnect->hSynStmt)) == SQL_STILL_EXECUTING) 
			{
			if(synFlag)
				{
				hConnect->synchronize_flag = (num+1)*10+2;
				return(SQL_StillExecuting);
				}
				
			if (wdPeekMessage()) 
				{
				SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
				return(0);
				}
			}
			
		hConnect->synchronize_flag = 0;
	
		if (retcode == SQL_NO_DATA_FOUND) 
			break;
			
		if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) 
			{
			wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
			SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
			return(0);
			}
	
syn3:
		while ((retcode = SQLGetData(hConnect->hSynStmt, num, SQL_C_CHAR,
			hConnect->lpSynBuf, (SDWORD)256, &(hConnect->synLength)))
			== SQL_STILL_EXECUTING) 
			{
			if(synFlag)
				{
				hConnect->synchronize_flag = (num+1)*10+3;
				return(SQL_StillExecuting);
				}
				
			if (wdPeekMessage()) 
				{
				SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
				return(0);
				}
			}
			
		hConnect->synchronize_flag = 0;
	
		if (retcode == SQL_NO_DATA_FOUND) 
			break;
			
		if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) 
			{
			wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
			SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
			return(0);
			} 

		if(wdGetSchemaString(&(hConnect->lpSchemaTable), (DBCOL **)&(hConnect->lpSynPtr), 
			hConnect->lpSynBuf)==0)
			{
			SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
			return(SQL_OutOfMemory);
			}
		}
	
	SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
	
	if(hConnect->synNum)
		return(hConnect->synNum);

	return(SQL_NoMoreData);
}

/*************************************************************************
*
*       wdgetInfo -- to get infomation from the driver
*
*************************************************************************/
SWORD wdGetInfo(ODBCHNDL *hConnect, UWORD type, LPSTR lpBuf)
{
	RETCODE retcode;
	SWORD   i;
	DBCOL   *lpColLast;
	
	retcode = SQLGetInfo(hConnect->hdbc, type, lpBuf, 256, &i);
	if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO) 
		{
		wdSQLError(henv, hConnect->hdbc, NULL);
		return(0);
		} 
		
	if(*lpBuf == 0)
		return(SQL_NoMoreData);
		
	lpColLast = NULL;
	if(wdGetSchemaString(&(hConnect->lpSchemaTable), &lpColLast, lpBuf)==0)
		return(SQL_OutOfMemory);
		
	return(1);
}


static char wildCard[2] = "%";
									
/*************************************************************************
*
*       sql_get_schema -- the entrance of SQLGetSchema function
*
*************************************************************************/
SWORD WINAPI _loadds SQLGetSchema(SWORD connection_num, SWORD type_num, LPSTR qualifier_text)
{
	ODBCHNDL *hConnect;
	RETCODE  retcode;
	SWORD    i,length, junk;
	DLLLIST  *dlllist_temp;
	
	dlllist_temp = wdGetVBlock(TRUE);
	if (dlllist_temp == NULL)
		{
		if(henv)
			return(SQL_OutOfMemory);
		else
			return(0);
		}
	
	hConnect = wdCheckConnect(dlllist_temp->hODBC, connection_num);
	if(hConnect == NULL)
		return(SQL_NoMoreData);
	
	switch(hConnect->synchronize_flag)
		{
		case 0: 
			break;
		case 21:
		case 22:
		case 23: 
			goto syn2;
		case 31:
		case 32:
		case 33: 
			goto syn3;
		case 41:
		case 42:
		case 43: 
			goto syn4;
		case 51: 
			goto syn51;
		case 52: 
			goto syn52;
		case 53: 
			goto syn53;
		default: 
			return(SQL_SynchronizeError);
		}
	
	if(hConnect->lpSchemaTable)
		{
		wdFreeSchemaTable(hConnect->lpSchemaTable);
		hConnect->lpSchemaTable = NULL;
		}
		
	if(!(hConnect->lpSynBuf = malloc(256)))
		{
		wdMessageBox(OUTOFMEMORY);
		return(SQL_OutOfMemory);
		}
	
	hConnect->lpSynPtr = NULL;
	switch(type_num) 
		{
		case 1:
			i = 0;
			retcode = SQLDataSources(henv, SQL_FETCH_FIRST,
					hConnect->lpSynBuf, 256, &length, (LPSTR)szResource,
					256, &junk);
			while (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
				{
				if(wdGetSchemaString(&(hConnect->lpSchemaTable), 
					(DBCOL **)&(hConnect->lpSynPtr), hConnect->lpSynBuf)==0)
					{
					free(hConnect->lpSynBuf);
					return(SQL_OutOfMemory);
					}
					
				i ++;
				retcode = SQLDataSources(henv, SQL_FETCH_NEXT,
						hConnect->lpSynBuf, 256, &length, (LPSTR)szResource,
						256, &junk);
				}
				
			free(hConnect->lpSynBuf);
			if (retcode != SQL_NO_DATA_FOUND) 
				{
				wdSQLError(henv, NULL, NULL);
				return(0);
				}
			return(i);
			
		case 2:
syn2:
			i = wdGetTables(hConnect, NULL, NULL, 1 ,dlllist_temp->synFlag);
			if(i==SQL_StillExecuting)
				return(i); 
				
			free(hConnect->lpSynBuf);
			if(i > 0)
				i = wdDeleteSameName(hConnect);
			return(i);
			
		case 3:
			_fstrcpy(hConnect->lpSynBuf,qualifier_text);
syn3:
			if (_fstrlen(hConnect->lpSynBuf)==0)
				i = wdGetTables(hConnect, hConnect->lpSynBuf, wildCard, 2, dlllist_temp->synFlag);
			else														   
				i = wdGetTables(hConnect, hConnect->lpSynBuf, NULL, 2, dlllist_temp->synFlag);

			if(i==SQL_StillExecuting)
				return(i);
				
			free(hConnect->lpSynBuf);
			if(i > 0)
				i = wdDeleteSameName(hConnect);
			return(i);
			
		case 4:
 			_fstrcpy(hConnect->lpSynBuf,qualifier_text);

			hConnect->lpSynPtr = _fstrchr(hConnect->lpSynBuf, '.');
			if(hConnect->lpSynPtr)
				{
				*(hConnect->lpSynPtr) = 0;
				(hConnect->lpSynPtr)++;
				}
syn4:
			i = wdGetTables(hConnect, hConnect->lpSynBuf, hConnect->lpSynPtr, 3, dlllist_temp->synFlag);
			if(i==SQL_StillExecuting)
				return(i);
			free(hConnect->lpSynBuf);
			return(i);
			
		case 5:
			if (SQLAllocStmt(hConnect->hdbc, &(hConnect->hSynStmt)) != SQL_SUCCESS) 
				{
				wdSQLError(henv, hConnect->hdbc, NULL);
				free(hConnect->lpSynBuf);
				return(0);
				}
				
			_fstrcpy(hConnect->lpSynBuf,qualifier_text);
syn51:
			while ((retcode = SQLColumns(hConnect->hSynStmt, NULL, 0, NULL, 0,
				hConnect->lpSynBuf, SQL_NTS, NULL, 0)) == SQL_STILL_EXECUTING) 
				{
				if(dlllist_temp->synFlag)
					{
					hConnect->synchronize_flag=51;
					return(SQL_StillExecuting);
					}
				if (wdPeekMessage()) 
					{
					SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
					free(hConnect->lpSynBuf);
					return(0);
					}
				}
			hConnect->synchronize_flag = 0;
	
			if (retcode != SQL_SUCCESS) 
				{
				wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
				SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
				free(hConnect->lpSynBuf);
				return(0);
				}
				
			for (hConnect->synNum = 0;; hConnect->synNum++) 
				{
syn52:
				while((retcode = SQLFetch(hConnect->hSynStmt)) == SQL_STILL_EXECUTING) 
					{
					if(dlllist_temp->synFlag)
						{
						hConnect->synchronize_flag=52;
						return(SQL_StillExecuting);
						}
					if (wdPeekMessage()) 
						{
						SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
						free(hConnect->lpSynBuf);
						return(0);
						}
					}
				hConnect->synchronize_flag = 0;
	
				if (retcode == SQL_NO_DATA_FOUND)
					{
					SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
					free(hConnect->lpSynBuf);
					if(hConnect->synNum)
					return((hConnect->synNum)<<1);
					return(SQL_NoMoreData);
					}
				if (retcode!=SQL_SUCCESS && retcode!=SQL_SUCCESS_WITH_INFO) 
					{
					wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
					SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
					free(hConnect->lpSynBuf);
					return(0);
					}
	
				for (hConnect->j = 4; hConnect->j <= 6; (hConnect->j)+=2) 
				/* column 4 holds the column_name data */
					{
syn53:
					while ((retcode = SQLGetData(hConnect->hSynStmt, hConnect->j, SQL_C_CHAR,
						hConnect->lpSynBuf, (SDWORD)256, &(hConnect->synLength)))
						== SQL_STILL_EXECUTING) 
						{
						if(dlllist_temp->synFlag)
							{
							hConnect->synchronize_flag=53;
							return(SQL_StillExecuting);
							}
						if (wdPeekMessage()) 
							{
							SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
							free(hConnect->lpSynBuf);
							return(0);
							}
						}
					hConnect->synchronize_flag = 0;
	
					if (retcode == SQL_NO_DATA_FOUND)
						{
						SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
						free(hConnect->lpSynBuf);
						if(hConnect->j==4)
							return((hConnect->synNum)<<1);
						return(((hConnect->synNum)<<1)+1);
						}
						
					if (retcode!=SQL_SUCCESS && retcode!=SQL_SUCCESS_WITH_INFO) 
						{
						wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
						SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
						free(hConnect->lpSynBuf);
						return(0);
						}
						
					if(wdGetSchemaString(&(hConnect->lpSchemaTable), 
						(DBCOL **)&(hConnect->lpSynPtr), hConnect->lpSynBuf)==0)
						{
						SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
						free(hConnect->lpSynBuf);
						return(SQL_OutOfMemory);
						}
					}
				}
			i = 0;
			break;
			
		case 6:
			i = wdGetInfo(hConnect, SQL_USER_NAME, hConnect->lpSynBuf);
			break;
			
		case 7:
			i = wdGetInfo(hConnect, SQL_DATABASE_NAME, hConnect->lpSynBuf);
			break;
			
		case 8:
			i = wdGetInfo(hConnect, SQL_DATA_SOURCE_NAME, hConnect->lpSynBuf);
			break;
			
		case 9:
			i = wdGetInfo(hConnect, SQL_DBMS_NAME, hConnect->lpSynBuf);
			break;
			
		case 10:
			i = wdGetInfo(hConnect, SQL_SERVER_NAME, hConnect->lpSynBuf);
			break;
			
		case 11:
			i = wdGetInfo(hConnect, SQL_OWNER_TERM, hConnect->lpSynBuf);
			break;
		
		case 12:
			i = wdGetInfo(hConnect, SQL_TABLE_TERM, hConnect->lpSynBuf);
			break;
		
		case 13:
			i = wdGetInfo(hConnect, SQL_QUALIFIER_TERM, hConnect->lpSynBuf);
			break;
		
		case 14:
			i = wdGetInfo(hConnect, SQL_PROCEDURE_TERM, hConnect->lpSynBuf);
			break;
		
		default:
			i =0;
			break;
		}
	
	free(hConnect->lpSynBuf);
	return(i);
}

/* return 0 means error, 1 means OK */
SWORD wdGetSchemaString(DBCOL * * lppSchemaTable, DBCOL * * lppSchemaLast, LPSTR lpBuf)
{
    DBCOL *lpSchemaCurr;

	if(lpSchemaCurr=(DBCOL *)malloc(sizeof(DBCOL)))
		lpSchemaCurr->next = NULL;
	else
		{
		wdMessageBox(OUTOFMEMORY);
		return(0);
		}
		
	if(*lppSchemaLast)
		(*lppSchemaLast)->next = lpSchemaCurr;
	else
		(*lppSchemaTable) = lpSchemaCurr;
		
	*lppSchemaLast = lpSchemaCurr;
	if(lpSchemaCurr->item=(LPSTR)malloc(_fstrlen(lpBuf)+1))
		_fstrcpy(lpSchemaCurr->item, lpBuf);
	else
		{
		wdMessageBox(OUTOFMEMORY);
		return(0);
		}
		
	return(1);
}

/*************************************************************************
*
*       sql_get_schema_text -- the entrance of SQLGetSchemaText function
*
*************************************************************************/
LPSTR WINAPI _loadds SQLGetSchemaItem$(SWORD connection_num, SWORD item_num)
{
	ODBCHNDL    *hConnect;
	DLLLIST     *dlllist_temp;
	DBCOL       *lpSchemaCurr;
	
	if(item_num < 1)
		return(wdNullString());
		
	dlllist_temp = wdGetVBlock(TRUE);
	if (dlllist_temp == NULL) 
		return(wdNullString());
	
	hConnect = wdCheckConnect(dlllist_temp->hODBC, connection_num);
	if(hConnect == NULL)
		return(wdNullString());
		
	if(hConnect->synchronize_flag)
		return(wdNullString());
		
	if(hConnect->lpSchemaTable)
		{
		lpSchemaCurr = hConnect->lpSchemaTable;
		while(--item_num)
			{
			if(lpSchemaCurr)
				lpSchemaCurr = lpSchemaCurr->next;
			else
				break;
			}
		if(lpSchemaCurr)
			{
			if(lpSchemaCurr->item)
				return(lpSchemaCurr->item);
			else
				return(wdNullString());
			}
		}
	return(wdNullString());
}

SWORD wdDeleteSameName(ODBCHNDL *hConnect)
{
	SWORD   i,same;
	DBCOL   *lpSchemaCurr, *lpSchemaLast;
	
	i = 0;
	if(hConnect->lpSchemaTable)
		{
		i = 1;
		lpSchemaCurr = hConnect->lpSchemaTable->next;
		while(lpSchemaCurr)
			{
			lpSchemaLast = hConnect->lpSchemaTable;
			same = 0;
			while(TRUE)
				{
				if(same==0)
					{
					if(_fstrcmp(lpSchemaLast->item,lpSchemaCurr->item)==0)
						same = 1;
					}
				if(lpSchemaLast->next==lpSchemaCurr)
					break;
				lpSchemaLast = lpSchemaLast->next;
				}
					
			if(same)
				{
				lpSchemaLast->next = lpSchemaCurr->next;
				free(lpSchemaCurr->item);
				free(lpSchemaCurr);
				lpSchemaCurr = lpSchemaLast->next;
				}
			else
				{
				i++;
				lpSchemaCurr = lpSchemaCurr->next;
				}
			}
		}
		
	if(i==1 && *(hConnect->lpSchemaTable->item) == 0)
		{
		wdFreeSchemaTable(hConnect->lpSchemaTable);
		hConnect->lpSchemaTable = NULL;
		return(SQL_NoMoreData);
		}
		
	return(i);
}

/*************************************************************************
*
*       sql_get_type_info -- the entrance of SQLGetTypeInfo function
*
*************************************************************************/
LPSTR WINAPI _loadds SQLGetTypeInfo$(SWORD connection_num, LPSTR type_text)
{
	ODBCHNDL *hConnect;
	SWORD    type;
	RETCODE  retcode;
	DLLLIST  *dlllist_temp;
	
	if((type=wdCheckTypeText(type_text))==0)
		return(wdNullString());
		
	dlllist_temp = wdGetVBlock(TRUE);
	if (dlllist_temp == NULL) 
		return(wdNullString());
	
	hConnect = wdCheckConnect(dlllist_temp->hODBC, connection_num);
	if(hConnect == NULL)
		return(wdNullString());
		
	if(hConnect->synchronize_flag)
		return(wdNullString());
		
	if (SQLAllocStmt(hConnect->hdbc, &(hConnect->hSynStmt)) != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, NULL);
		return(wdNullString());
		}
		
	while ((retcode = SQLGetTypeInfo(hConnect->hSynStmt, type)) == SQL_STILL_EXECUTING) 
		{
		if (wdPeekMessage()) 
			{
			SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
			return(wdNullString());
			}
		}
	
	if (retcode != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
		SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
		return(wdNullString());
		}
		
	while((retcode = SQLFetch(hConnect->hSynStmt)) == SQL_STILL_EXECUTING) 
		{
		if (wdPeekMessage()) 
			{
			SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
			return(wdNullString());
			}
		}
		
	if (retcode == SQL_NO_DATA_FOUND)
		{
		SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
		return(wdNullString());
		}
		
	if (retcode!=SQL_SUCCESS && retcode!=SQL_SUCCESS_WITH_INFO) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
		SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
		return(wdNullString());
		}
		
	while ((retcode = SQLGetData(hConnect->hSynStmt, 1, SQL_C_CHAR,
		(LPSTR)szResource, (SDWORD)256, &(hConnect->synLength)))
		== SQL_STILL_EXECUTING) 
		{
		if (wdPeekMessage()) 
			{
			SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
			return(wdNullString());
			}
		}
		
	if (retcode == SQL_NO_DATA_FOUND)
		{
		SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
		return(wdNullString());
		}
		
	if (retcode!=SQL_SUCCESS && retcode!=SQL_SUCCESS_WITH_INFO) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hSynStmt);
		SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
		return(wdNullString());
		}
		
	SQLFreeStmt(hConnect->hSynStmt, SQL_DROP);
	return((LPSTR)szResource);
}

static LPSTR SQLTypes[TOTALTYPE] = {
	"bigint",
	"binary",
	"bit",
	"char",
	"date",
	"decimal",
	"double",
	"float",
	"integer",
	"longvarbinary",
	"longvarchar",
	"numeric",
	"real",
	"smallint",
	"time",
	"timestamp",
	"tinyint",
	"varbinary",
	"varchar"
};

SWORD wdCheckTypeText(LPSTR type_text)
{
	SWORD   i;
	
	for(i=0; i < TOTALTYPE; i++)
		{
		if(_fstricmp(type_text, SQLTypes[i])== 0)
			return(matchType[i]);
		}
	return(0);
}
