/*************************************************************************
*
*       exequery.c              6/10/93
*
*
*       This file is about SQLExecQuery for WinWord ODBC
*
*       3-11-94  Bug Fix for large data problem
*       8/23/95 - added SQLQueryFetch
*                 fixed 256 character limit
*                 now returns number of rows updated, inserted, or deleted
*
*************************************************************************/

#include "wbodbc.h"
#include <string.h>

static int IsUpdDelIns(LPSTR query)
/* returns 1 if _query_ contains "UPDATE", "INSERT", or "DELETE"
	called by SQLExecQuery and SQLQueryExec */
{
	static char 	upd[7]="UPDATE";
	static char 	del[7]="DELETE";
	static char 	ins[7]="INSERT";
	LPSTR	inStr;
	int		queryLen;
	int		retval;

	queryLen = _fstrlen(query)+1;
	inStr = (LPSTR)calloc(1,queryLen);
	_fstrncpy(inStr,query,6);

	retval = (_strnicmp(inStr,upd,6)==0) || (_strnicmp(inStr,del,6)==0)
				|| (_strnicmp(inStr,ins,6)==0);

	free(inStr);
	return retval;
} /* IsUpdDelIns() */


/*************************************************************************
*
*       sql_execquery -- the entrance of SQLExecQuery function
*
*************************************************************************/
SWORD WINAPI _loadds SQLExecQuery(SWORD connection_num, LPSTR query_text)
{
	ODBCHNDL	*hConnect;
	RETCODE		retcode;
	DLLLIST		*dlllist_temp;
	SDWORD		rowcnt; // number of rows affected by INSERT,DELETE,or UPDATE
	
	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 1: 
			goto syn1;
		case 2: 
			goto syn2;
		case 3: 
			goto syn3;
		case 4:
		case 5:
			goto syn4;
		default:
			return(SQL_SynchronizeError);
		}
		
	if(hConnect->lpDBList)
		{
		wdFreeDBList(hConnect->lpDBList);
		hConnect->lpDBList = NULL;
		}
	
	if (hConnect->hstmt == 0) 
		{
		if (SQLAllocStmt(hConnect->hdbc, &(hConnect->hstmt)) != SQL_SUCCESS) 
			{
			wdSQLError(henv, hConnect->hdbc, NULL);
			return(0);
			}
		} 
	else 
		{
		if (SQLFreeStmt(hConnect->hstmt, SQL_CLOSE) != SQL_SUCCESS) 
			{
			wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
			return(0);
			}
		}
		
	if(hConnect->lpSynBuf=malloc(_fstrlen(query_text)+1))
		_fstrcpy(hConnect->lpSynBuf, query_text);
	else
		{
		wdMessageBox(OUTOFMEMORY);
		return(SQL_OutOfMemory);
		}
	
syn1:
	while ((retcode = SQLPrepare(hConnect->hstmt, hConnect->lpSynBuf, 
									SQL_NTS)) == SQL_STILL_EXECUTING) 
		{
		/* Do some PeekMessage */
		if(dlllist_temp->synFlag)
			{
			hConnect->synchronize_flag=1;
			return(SQL_StillExecuting);
			}
			
		if(wdPeekMessage())
			{
			return(0);
			}
		}
		
	free(hConnect->lpSynBuf);
	hConnect->synchronize_flag=0;
	
	if (retcode != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
		return(0);
		}
	
syn2:
	while ((retcode = SQLExecute(hConnect->hstmt)) == SQL_STILL_EXECUTING) 
		{
		if(dlllist_temp->synFlag)
			{
			hConnect->synchronize_flag=2;
			hConnect->lpSynBuf = NULL;
			return(SQL_StillExecuting);
			}
			
		/* Do some PeekMessage */
		if(wdPeekMessage())
			{
			hConnect->synchronize_flag=0;
			return(0);
			}
		}
	
	hConnect->synchronize_flag=0;
	if (retcode != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
		return(0);
		}
		
syn3:
	while ((retcode = SQLNumResultCols(hConnect->hstmt, 
							&(hConnect->synNum))) == SQL_STILL_EXECUTING) 
		{
		if(dlllist_temp->synFlag)
			{
			hConnect->synchronize_flag=3;
			hConnect->lpSynBuf = NULL;
			return(SQL_StillExecuting);
			}
			
		/* Do some PeekMessage */
		if(wdPeekMessage())
			{
			hConnect->synchronize_flag=0;
			return(0);
			}
		}
	
	hConnect->synchronize_flag=0;
	if (retcode != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
		return(0);
		}
	
	if (hConnect->synNum>0) {
syn4:
		if((retcode=wdGetDBList(hConnect,hConnect->synNum,dlllist_temp->synFlag))==0)
			return(0);
			
		if(retcode<0)
			return(retcode);
			
		return(hConnect->lpDBList->ncols);
	}

	/* did not return any columns in result set, If query was UPDATE, INSERT,
		or DELETE, return number of rows effected. Otherwise return 1 */
	if (IsUpdDelIns(query_text)) {
		retcode=SQLRowCount(hConnect->hstmt,&rowcnt);
		if (retcode!=SQL_SUCCESS) {
		 	wdSQLError(henv,hConnect->hdbc,hConnect->hstmt);
			return (0);
		} else
			return ((SWORD)rowcnt);
	} else	 
		return 1;
}

/* return 0 means error, return 1 means OK */
SWORD wdGetDBList(ODBCHNDL *hConnect, SWORD col, SWORD synFlag)
{
	RETCODE	retcode;
	DBROW	*lpRowCurr;
	LPSTR	lpCurr;
	short	len;
	SDWORD	colLength;  // size of results buffer
	LPSTR	lpTemp;     // temporary buffer for realloc
	
	switch(hConnect->synchronize_flag)
		{
		case 0:
			break;
		case 4:
			goto syn4;
		case 5:
			goto syn5;
		default:
			return(SQL_SynchronizeError);
		}
		
	if(hConnect->lpDBList=(DBLIST *)malloc(sizeof(DBLIST)))
		hConnect->lpDBList->ncols = col;
	else
		{
		wdMessageBox(OUTOFMEMORY);
		return(SQL_OutOfMemory);
		}
		
	hConnect->lpDBList->nrows = 0;
	hConnect->lpDBList->lpRow = NULL;
	hConnect->lpDBList->hCurrLock = NULL;
	hConnect->lpDBList->currOffset = 0;
	
	if(hConnect->lpSynBuf = malloc(256))
		hConnect->lpSynPtr = NULL;
	else
		{
		wdMessageBox(OUTOFMEMORY);
		return(SQL_OutOfMemory);
		}
	
	while(TRUE)
		{
syn4:
		while((retcode=SQLFetch(hConnect->hstmt))==SQL_STILL_EXECUTING)
			{
			if(synFlag)
				{
				hConnect->synchronize_flag=4;
				return(SQL_StillExecuting);
				}
				
			if(wdPeekMessage())
				{
				hConnect->synchronize_flag=0;
				free(hConnect->lpSynBuf);
				return(0);
				}
			}
			
		hConnect->synchronize_flag=0;
		
		if(retcode==SQL_NO_DATA_FOUND)
			{
			free(hConnect->lpSynBuf);
			if(hConnect->lpDBList->hCurrLock)
				{
				GlobalUnlock(hConnect->lpDBList->hCurrLock);
				hConnect->lpDBList->hCurrLock = NULL;
				}
			return(1);
			}
			
		if(retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
			{
			free(hConnect->lpSynBuf);
			wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
			return(0);
			}
			
		if(lpRowCurr = (DBROW *)malloc(sizeof(DBROW)))
			lpRowCurr->hGlobal = hConnect->lpDBList->hCurrLock;
		else
			{
			free(hConnect->lpSynBuf);
			wdMessageBox(OUTOFMEMORY);
			return(SQL_OutOfMemory);
			}
			
		lpRowCurr->next = NULL;
		lpRowCurr->offset = -1;
		
		if(hConnect->lpSynPtr)
			((DBROW *)(hConnect->lpSynPtr)) -> next = lpRowCurr;
		else
			hConnect->lpDBList->lpRow = lpRowCurr; 
			
		hConnect->lpSynPtr = (LPSTR)lpRowCurr;
		lpCurr = wdGetSpace(hConnect->lpDBList, lpRowCurr, (SWORD) ((hConnect->lpDBList->ncols)<<1));
		if(lpCurr==NULL)
			{
			free(hConnect->lpSynBuf);
			return(SQL_OutOfMemory);
			}
			
		_fmemset(lpCurr, 0, (hConnect->lpDBList->ncols)<<1);
		hConnect->lpDBList->nrows ++;
		for(hConnect->synNum=1; hConnect->synNum<=hConnect->lpDBList->ncols; hConnect->synNum++)
			{
syn5:
			/* calculate size of column buffer and readjust its size */
			colLength=GetColSize(hConnect,hConnect->synNum);
			if ((colLength<=0) || (colLength>kMaxCellSize))
				colLength=kMaxCellSize;		// default to maximum size on error
			else
				colLength++;				// add one for null termination
			lpTemp = (LPSTR)realloc(hConnect->lpSynBuf,(SWORD)colLength);
			if (lpTemp==NULL) {
				wdMessageBox(OUTOFMEMORY);
				return(SQL_OutOfMemory);
			}		
			hConnect->lpSynBuf = lpTemp;
			_fmemset(hConnect->lpSynBuf,'\0',1);

			while((retcode=SQLGetData(hConnect->hstmt, hConnect->synNum, (SWORD)SQL_C_CHAR, 
				hConnect->lpSynBuf, colLength, &(hConnect->synLength)))==SQL_STILL_EXECUTING)
				{
				if(synFlag)
					{
					hConnect->synchronize_flag=5;
					return(SQL_StillExecuting);
					}
					
				if(wdPeekMessage())
					{
					hConnect->synchronize_flag=0;
					free(hConnect->lpSynBuf);
					return(0);
					}
				}		/* while (SQL_STILL_EXECUTING) */
				
			hConnect->synchronize_flag=0;

			if(retcode == SQL_NO_DATA_FOUND)
				break;
				
			if(retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO)
				{
				free(hConnect->lpSynBuf);
				wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
				return(0);
				}

							
			len = _fstrlen(hConnect->lpSynBuf)+1;

			lpCurr = wdGetSpace(hConnect->lpDBList, (DBROW *)(hConnect->lpSynPtr), len);
			if(lpCurr==NULL)
				{
				free(hConnect->lpSynBuf);
				return(SQL_OutOfMemory);
				}
				
		_fmemcpy(lpCurr, hConnect->lpSynBuf, len);
		*(SWORD *)(hConnect->lpDBList->lpCurrLock+((DBROW *)(hConnect->lpSynPtr))->offset+((hConnect->synNum)<<1)-2) = hConnect->lpDBList->currOffset-len-((DBROW *)(hConnect->lpSynPtr))->offset;
		}
	}
	return (1);
}				 

LPSTR wdGetSpace(DBLIST *lpDBList, DBROW *lpRow, SWORD size)
{
	LPSTR   lpCurr;
	SWORD   i;
	
	if(lpDBList->hCurrLock)
		{
		if(lpDBList->currOffset+(long)size <= BLOCKSIZE)
			{
			if(lpRow->offset<0)
				{
				lpRow->hGlobal = lpDBList->hCurrLock;
				lpRow->offset = lpDBList->currOffset;
				}
			lpCurr = lpDBList->lpCurrLock+lpDBList->currOffset;
			lpDBList->currOffset += size;
			return(lpCurr);
			}
		}
		
	if(lpRow->hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,BLOCKSIZE))
		lpCurr = GlobalLock(lpRow->hGlobal);
	else
		{
		wdMessageBox(OUTOFMEMORY);
		return(NULL);
		}
	
	i = 0;
	
	if(lpDBList->hCurrLock)
		{
		if(lpRow->offset>=0)
			{
			i = lpDBList->currOffset - lpRow->offset;
			_fmemcpy(lpCurr,lpDBList->lpCurrLock+lpRow->offset,i);
			}
		GlobalUnlock(lpDBList->hCurrLock);
		}
		
	lpRow -> offset = 0;
	lpDBList->hCurrLock = lpRow -> hGlobal;
	lpDBList->lpCurrLock = lpCurr;
	lpCurr += i;
	lpDBList -> currOffset = i + size;
	return(lpCurr);
}


/*************************************************************************
*
*       SQLQueryExec - same functionality as SQLExecQuery, but doesn't store
*						result set in memory. Should never be used with
*						SQLRetrieveItem$ or SQLRetrieveToDocument
*
*						added 7/29/95
*************************************************************************/
SWORD WINAPI _loadds SQLQueryExec(SWORD connection_num, LPSTR query_text)
{
	ODBCHNDL	*hConnect;
	RETCODE		retcode;
	DLLLIST		*dlllist_temp;
	SDWORD		rowcnt;
	
	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 1: 
			goto syn1;
		case 2: 
			goto syn2;
		case 3: 
			goto syn3;
		case 4:
		case 5:
			goto syn4;
		default:
			return(SQL_SynchronizeError);
		}
		
	if(hConnect->lpDBList)
		{
		wdFreeDBList(hConnect->lpDBList);
		hConnect->lpDBList = NULL;
		}
	
	if (hConnect->hstmt == 0) 
		{
		if (SQLAllocStmt(hConnect->hdbc, &(hConnect->hstmt)) != SQL_SUCCESS) 
			{
			wdSQLError(henv, hConnect->hdbc, NULL);
			return(0);
			}
		} 
	else 
		{
		if (SQLFreeStmt(hConnect->hstmt, SQL_CLOSE) != SQL_SUCCESS) 
			{
			wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
			return(0);
			}
		}
		
	if(hConnect->lpSynBuf=malloc(_fstrlen(query_text)+1))
		_fstrcpy(hConnect->lpSynBuf, query_text);
	else
		{
		wdMessageBox(OUTOFMEMORY);
		return(SQL_OutOfMemory);
		}

	/* Check to see if scrollable cursors are allowed */
	if (SupportsScrollableCursors(hConnect)) {
		retcode = SQLSetStmtOption(hConnect->hstmt,SQL_CURSOR_TYPE,SQL_CURSOR_STATIC);
		if (retcode != SQL_SUCCESS)
			SQLSetStmtOption(hConnect->hstmt,SQL_CURSOR_TYPE,SQL_CURSOR_FORWARD_ONLY);
	}

	
syn1:
	while ((retcode = SQLPrepare(hConnect->hstmt, hConnect->lpSynBuf, 
									SQL_NTS)) == SQL_STILL_EXECUTING) 
		{
		/* Do some PeekMessage */
		if(dlllist_temp->synFlag)
			{
			hConnect->synchronize_flag=1;
			return(SQL_StillExecuting);
			}
			
		if(wdPeekMessage())
			{
			return(0);
			}
		}
		
	free(hConnect->lpSynBuf);
	hConnect->synchronize_flag=0;
	
	if (retcode != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
		return(0);
		}


	
syn2:
	while ((retcode = SQLExecute(hConnect->hstmt)) == SQL_STILL_EXECUTING) 
		{
		if(dlllist_temp->synFlag)
			{
			hConnect->synchronize_flag=2;
			hConnect->lpSynBuf = NULL;
			return(SQL_StillExecuting);
			}
			
		/* Do some PeekMessage */
		if(wdPeekMessage())
			{
			hConnect->synchronize_flag=0;
			return(0);
			}
		}
	
	hConnect->synchronize_flag=0;
	if (retcode != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
		return(0);
		}

		
syn3:
	while ((retcode = SQLNumResultCols(hConnect->hstmt, 
							&(hConnect->synNum))) == SQL_STILL_EXECUTING) 
		{
		if(dlllist_temp->synFlag)
			{
			hConnect->synchronize_flag=3;
			hConnect->lpSynBuf = NULL;
			return(SQL_StillExecuting);
			}
			
		/* Do some PeekMessage */
		if(wdPeekMessage())
			{
			hConnect->synchronize_flag=0;
			return(0);
			}
		}
	
	hConnect->synchronize_flag=0;
	if (retcode != SQL_SUCCESS) 
		{
		wdSQLError(henv, hConnect->hdbc, hConnect->hstmt);
		return(0);
		}
	
	if (hConnect->synNum>0) 
		{
syn4:
		/* allocate DBList so SQLRetrieveColumns and SQLRetrieveRows
			will still work */
		if(hConnect->lpDBList=(DBLIST *)malloc(sizeof(DBLIST)))
			hConnect->lpDBList->ncols = hConnect->synNum;
		else {
			wdMessageBox(OUTOFMEMORY);
			return(SQL_OutOfMemory);
		}
		
		hConnect->lpDBList->nrows = 0;
		hConnect->lpDBList->lpRow = NULL;
		hConnect->lpDBList->hCurrLock = NULL;
		hConnect->lpDBList->lpCurrLock = NULL;
		hConnect->lpDBList->currOffset = 0;

		
		return(hConnect->lpDBList->ncols);
		}
	/* did not return any columns in result set, If query was UPDATE, INSERT,
		or DELETE, return number of rows effected. Otherwise return 1 */
	if (IsUpdDelIns(query_text)) {
		retcode=SQLRowCount(hConnect->hstmt,&rowcnt);
		if (retcode!=SQL_SUCCESS) {
		 	wdSQLError(henv,hConnect->hdbc,hConnect->hstmt);
			return (0);
		} else
			return ((SWORD)rowcnt);
	} else
		return 1;
} /* SQLQueryExec() */



