QUIKTEST.C

//--------------------------------------------------------------------------------------- 
//
// PROGRAM: quiktest.c
// This code is furnished on an as-is basis as part of the ODBC SDK and is
// intended for example purposes only.
//
// PURPOSE: This is a Quick Test of the basic functionality of an ODBC driver.
//
// FUNCTIONS:
// QuickTest() - performs the quick test focusing on basic functionalities.
//
//--------------------------------------------------------------------------------------------
#pragma warning (disable : 4703)

#define QUIKTEST

#include "autotest.h"
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <wchar.h>
#include <tchar.h>
#include <time.h>
// #include <crtdbg.h>
#include "sql.h"
#include "sqlext.h"
#include "quiktest.h"

//--------------------------------------------------------------------------------------
// static
//--------------------------------------------------------------------------------------
static TCHAR vszFile[]= TEXT(__FILE__);

static LPTSTR sz07009= TEXT("07009");
static LPTSTR szIM001= TEXT("IM001");
static LPTSTR szHYC00= TEXT("HYC00");
static LPTSTR szHY009= TEXT("HY009");
static LPTSTR szHY091= TEXT("HY091");
static LPTSTR szHY092= TEXT("HY092");
static LPTSTR szISO= TEXT("ISO 9075");
static LPTSTR szODBC30= TEXT("ODBC 3.0");
static LPTSTR szNotSupported= TEXT("did not return Not supported message");
static LPTSTR szSQLSETCONNECTATTR= TEXT("SQLSetConnectAttr");
static LPTSTR szSQLGETCONNECTATTR= TEXT("SQLGetConnecTAttr");
static LPTSTR szSQLGETINFO= TEXT("SQLGetInfo");
static LPTSTR szSQLGETTYPEINFO= TEXT("SQLGetTypeInfo");
static LPTSTR szSQLALLOCHANDLE= TEXT("SQLAllocHandle");
static LPTSTR szSQLALLOCSTMT= TEXT("SQLAllocStmt");
static LPTSTR szSQLALLOCCONNECT= TEXT("SQLAllocConnect");
static LPTSTR szSQLALLOCENV= TEXT("SQLAllocEnv");
static LPTSTR szSQLSETSTMTATTR= TEXT("SQLSetStmtAttr");
static LPTSTR szSQLGETSTMTATTR= TEXT("SQLGetStmtAttr");
static LPTSTR szSQLSETSTMTOPTION= TEXT("SQLSetStmtOption");
static LPTSTR szSQLGETSTMTOPTION= TEXT("SQLGetStmtOption");
static LPTSTR szSQLSETCONNECTOPTION= TEXT("SQLSetConnectOption");
static LPTSTR szSQLGETCONNECTOPTION= TEXT("SQLGetConnectOption");
static LPTSTR szSQLFETCH= TEXT("SQLFetch");
static LPTSTR szSQLBINDCOL= TEXT("SQLBindCol");
static LPTSTR szSQLFOREIGNKEYS= TEXT("SQLForeignkeys");
static LPTSTR szSQLFREEHANDLE= TEXT("SQLFreeHandle");
static LPTSTR szSQLFREECONNECT= TEXT("SQLFreeConnect");
static LPTSTR szSQLFREEENV= TEXT("SQLFreeEnv");
static LPTSTR szSQLGETDESCREC= TEXT("SQLGetDescRec");
static LPTSTR szSQLSETDESCREC= TEXT("SQLSetDescRec");
static LPTSTR szSQLGETDESCFIELD= TEXT("SQLGetDescField");
static LPTSTR szSQLSETDESCFIELD= TEXT("SQLSetDescField");
static LPTSTR szSQLCOPYDESC= TEXT("SQLCopyDesc");
static LPTSTR szSQLSETENVATTR= TEXT("SQLSetEnvAttr");
static LPTSTR szSQLGETENVATTR= TEXT("SQLGetEnvAttr");
static LPTSTR szSQLFREESTMT= TEXT("SQLFreeStmt");
static LPTSTR szSQLENDTRAN= TEXT("SQLEndTran");
static LPTSTR szSQLGETCURSORNAME= TEXT("SQLGetCursorName");
static LPTSTR szSQLEXECDIRECT= TEXT("SQLExecDirect");
static LPTSTR szSQLPUTDATA= TEXT("SQLPutData");
static LPTSTR szSQLPARAMDATA= TEXT("SQLParamData");
static LPTSTR szSQLROWCOUNT= TEXT("SQLRowCount");
static LPTSTR szSQLSETPARAM= TEXT("SQLSetParam");
static LPTSTR szSQLBINDPARAMETER= TEXT("SQLBindParameter");
static LPTSTR szSQLBINDPARAM= TEXT("SQLBindParam");
static LPTSTR szSQLPREPARE= TEXT("SQLPrepare");
static LPTSTR szSQLEXECUTE= TEXT("SQLExecute");
static LPTSTR szSQLCOLATTRIBUTES= TEXT("SQLColAttributes");
static LPTSTR szSQLGETDATA= TEXT("SQLGetData");
static LPTSTR szSQLCOLUMNS= TEXT("SQLColumns");
static LPTSTR szSQLGETFUNCTIONS= TEXT("SQLGetFunctions");
static LPTSTR szSQLDRIVERCONNECT= TEXT("SQLDriverConnect");
static LPTSTR szSQLDISCONNECT= TEXT("SQLDisconnect");
static LPTSTR szSQLCONNECT= TEXT("SQLConnect");
static LPTSTR szSQLCANCEL= TEXT("SQLCancel");
static LPTSTR szSQLDESCRIBECOL= TEXT("SQLDescribeCol");
static LPTSTR szSQLTABLES= TEXT("SQLTables");
static LPTSTR szSQLSPECIALCOLUMNS= TEXT("SQLSpecialColumns");
static LPTSTR szSQLDESCRIBEPARAM= TEXT("SQLDescribeParam");
static LPTSTR szSQLNUMPARAMS= TEXT("SQLNumParams");
static LPTSTR szSQLPARAMOPTIONS= TEXT("SQLParamOptions");
static LPTSTR szSQLPRIMARYKEYS= TEXT("SQLPrimaryKeys");
static LPTSTR szSQLEXTENDEDFETCH= TEXT("SQLExtendedFetch");
static LPTSTR szSELECTSTAR= TEXT("select * from %s");
static LPTSTR szQUICKREBIND= TEXT("Quick Rebind");
static LPTSTR szSQLBROWSECONNECT= TEXT("SQLBrowseConnect");
static LPTSTR szBLANKLINE= TEXT(" ");
static LPTSTR szTAB= TEXT("\t");
static LPTSTR szSQLNUMRESULTCOLS= TEXT("SQLNumResultCols");
static LPTSTR szSQLSETSCROLLOPTIONS= TEXT("SQLSetScrollOptions");
static LPTSTR szSQLFETCHSCROLL= TEXT("SQLFetchScroll");
static LPTSTR szSQLSTATISTICS= TEXT("SQLStatistics");
static LPTSTR szSQLCLOSECURSOR= TEXT("SQLCloseCursor");
static LPTSTR szSQLGETDIAGREC= TEXT("SQLGetDiagRec");
static LPTSTR szSQLGETDIAGFIELD= TEXT("SQLGetDiagField");
static LPTSTR szRETDATA= TEXT("returned data");
static LPTSTR szINCORRECTOUTLEN= TEXT("incorrect outlen");
static LPTSTR szSQLNATIVESQL= TEXT("SQLNativeSql");
static LPTSTR szSQLNATIVESQLW= TEXT("SQLNativeSqlW");
static LPTSTR szSQLCONNECTW= TEXT("SQLConnectW");
static LPTSTR szSQLTABLESA= TEXT("SQLTablesA");
static LPTSTR szSQLTABLESW= TEXT("SQLTablesW");
static LPTSTR szSQLCOLUMNSA= TEXT("SQLColumnsA");
static LPTSTR szSQLCOLUMNSW= TEXT("SQLColumnsW");
static LPTSTR szSQLEXECDIRECTA= TEXT("SQLExecDirectA");
static LPTSTR szSQLEXECDIRECTW= TEXT("SQLExecDirectW");

LPTSTR iTestNames[]={
TEXT("Test Connection Options"),
TEXT("Test Statement Options"),
TEXT("Test SQLGetCursorName"),
TEXT("Test Data"),
TEXT("Test SQLNumResultCols"),
TEXT("Test Meta-Data"),
TEXT("Test Searched Query"),
TEXT("Test Large Query"),
TEXT("Test SQLTables"),
TEXT("Test SQLStatistics"),
TEXT("Test SQLSpecialColumns"),
TEXT("Test 'Like' Query"),
TEXT("Test SQLForeignKeys"),
TEXT("Test SQLBrowseConnect"),
TEXT("Test SQLDataSources"),
TEXT("Test SQLDrivers"),
TEXT("Test SQLMoreResults"),
TEXT("Test SQLNativeSQL"),
TEXT("Test SQLDescribeParam"),
TEXT("Test SQLNumParams"),
TEXT("Test SQLParamOptions"),
TEXT("Test SQLPrimaryKeys"),
TEXT("Test SQLProcedures"),
TEXT("Test SQLTablePrivileges"),
TEXT("Test SQLColumnPrivileges"),
TEXT("Test SQLSetScrollOptions"),
TEXT("Test SQLExtendedFetch"),
TEXT("Test SQL_OJ_CAPABILITIES"),
TEXT("Test SQLSetConnectAttr"),
TEXT("Test SQLSetStmtAttr"),
TEXT("Test Threading"),
TEXT("Test GetDescField"),
TEXT("Test SetDescField"),
TEXT("Test GetDescRec"),
TEXT("Test SetDescRec"),
TEXT("Test CopyDesc"),
TEXT("Test Descriptor Defaults"),
TEXT("Test Usage of Descriptor"),
TEXT("Test Environment Attributes"),
TEXT("Test SQLEndTran"),
TEXT("Test SQLBindParam"),
TEXT("Test Quick Rebind"),
TEXT("Test SQLFetchScroll"),
TEXT("Test GetDiagRec"),
TEXT("Test GetDiagField"),
TEXT("Test MixedAnsiUnicode")
};


typedef struct tagHANDLES
{
SWORDfHandleType;
SQLHANDLEhHandle;

} HANDLES, *lpHANDLES;

SUPPORTOPTINFO OptionList[] = {SQL_SO_FORWARD_ONLY, SQL_SCROLL_FORWARD_ONLY,
SQL_SO_KEYSET_DRIVEN, SQL_SCROLL_KEYSET_DRIVEN,
SQL_SO_DYNAMIC, SQL_SCROLL_DYNAMIC};

SUPPORTCONCURINFO ConcurList[] = {SQL_SCCO_READ_ONLY, SQL_CONCUR_READ_ONLY,
SQL_SCCO_LOCK, SQL_CONCUR_LOCK,
SQL_SCCO_OPT_TIMESTAMP, SQL_CONCUR_TIMESTAMP,
SQL_SCCO_OPT_VALUES, SQL_CONCUR_VALUES};


/*--------------------------------------------------------------------------------------*/
/* globals */
/*--------------------------------------------------------------------------------------*/
lpSERVERINFOlpSI = NULL;
UWORDfBindParameter=FALSE,
fDiagRecSupported=FALSE,
fDiagFieldSupported=FALSE,
uDriverODBCVer=0;
UWORDguwRowCount=0;// Count of rows in the automaketable

/* these are globals so that the error functions can access them without
needing to have them passed to every error check */
static HENV henv=SQL_NULL_HENV;
static HDBC hdbc=SQL_NULL_HDBC;
static HSTMT hstmt=SQL_NULL_HSTMT;
static SQLHDESC hdesc=SQL_NULL_HDESC;



//globals for test descriptors
TIMESTAMP_STRUCT tsval= {1955, 12, 31, 23, 59, 59, 999};
SQLINTEGER cbtsval=sizeof(TIMESTAMP_STRUCT);
SQLINTEGER cbValueMax, cbValue, swBindOffset=128;
SQLUINTEGER RowsProcessed;
SQLSMALLINT DescBuf[MAX_DESC_BUF];
SQLUSMALLINT StatusArray[STATUS_ARRAY_SIZE];
SQLTCHAR buf[MAX_STRING_SIZE];
SQLTCHAR szParamName[MAX_STRING_SIZE]=TEXT("Parameter Name");

struct tagDescType {
SQLUSMALLINTuwDescType;
SQLUSMALLINTuwTypeMask;
TCHAR szDescName[4];
} DescTypes[] = {
SQL_ATTR_APP_ROW_DESC,DESC_ARD,TEXT("ARD"),
SQL_ATTR_APP_PARAM_DESC,DESC_APD,TEXT("APD"),
SQL_ATTR_IMP_ROW_DESC,DESC_IRD,TEXT("IRD"),
SQL_ATTR_IMP_PARAM_DESC,DESC_IPD,TEXT("IPD")
};

struct tagDescInfo {
SQLSMALLINT uwDescField;
LPTSTR szDescFieldName;
BOOL fHeader;
SQLSMALLINT fGettable;// Can be retrieved for given descriptor types
SQLSMALLINT fSettable;// Can be set for given descriptor types
SQLSMALLINT fDefault;// Has a default value for the descriptor types
SQLINTEGER NewValue;
SQLINTEGER DefaultVal;
SQLSMALLINT cbValue;
SQLINTEGER size;
} rgDescInfo[] ={
// Test expects all header fields to be listed first. The TYPE field must be the first record field listed.
// size should be set to 0 for string fields, since we don't know up front what to set it to
// Field IDField NameHeaderGettableSettablefDefaultNew ValueDefault ValueData SizeData Type

//Header fields
SQL_DESC_COUNT,TEXT("SQL_DESC_COUNT"),TRUE,DESC_ALL,DESC_MOST,DESC_MOST,1,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_ALLOC_TYPE,TEXT("SQL_DESC_ALLOC_TYPE"),TRUE,DESC_ALL,DESC_NONE,DESC_ALL,0,SQL_DESC_ALLOC_AUTO,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_ARRAY_SIZE,TEXT("SQL_DESC_ARRAY_SIZE"),TRUE,DESC_AD,DESC_AD,DESC_AD,STATUS_ARRAY_SIZE,1,sizeof(SQLUINTEGER),SQL_IS_UINTEGER,
SQL_DESC_ARRAY_STATUS_PTR,TEXT("SQL_DESC_ARRAY_STATUS_PTR"),TRUE,DESC_ALL,DESC_ALL,DESC_ALL,(SQLINTEGER)(StatusArray),(SQLINTEGER)NULL,sizeof(SQLPOINTER),SQL_IS_POINTER,
SQL_DESC_BIND_OFFSET_PTR,TEXT("SQL_DESC_BIND_OFFSET_PTR"),TRUE,DESC_AD,DESC_AD,DESC_AD,(SQLINTEGER)(&swBindOffset),(SQLINTEGER)NULL,sizeof(SQLPOINTER),SQL_IS_POINTER,
SQL_DESC_BIND_TYPE,TEXT("SQL_DESC_BIND_TYPE"),TRUE,DESC_AD,DESC_AD,DESC_AD,BIND_SIZE,0,sizeof(SQLINTEGER),SQL_IS_INTEGER,
//SQL_DESC_OUT_OF_LINE_HEAP,TEXT("SQL_DESC_OUT_OF_LINE_HEAP"),TRUE,DESC_AD,DESC_AD,(SQLINTEGER)(StatusArray),sizeof(SQLINTEGER),SQL_IS_POINTER,
//SQL_DESC_OUT_OF_LINE_HEAP_OCTET_LENGTH,TEXT("SQL_DESC_OUT_OF_LINE_HEAP_OCTET_LENGTH"),TRUE, DESC_AD,DESC_AD,sizeof(StatusArray),sizeof(SQLINTEGER),SQL_IS_NOT_POINTER,
SQL_DESC_ROWS_PROCESSED_PTR,TEXT("SQL_DESC_ROWS_PROCESSED_PTR"),TRUE,DESC_ID,DESC_ID,DESC_ID,(SQLINTEGER)(&RowsProcessed),(SQLINTEGER)NULL,sizeof(SQLPOINTER),SQL_IS_POINTER,

// Record fields
SQL_DESC_TYPE,TEXT("SQL_DESC_TYPE"), FALSE,DESC_ALL,DESC_MOST,DESC_AD,SQL_DATETIME,SQL_C_DEFAULT,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_DATETIME_INTERVAL_CODE,TEXT("SQL_DESC_DATETIME_INTERVAL_CODE"),FALSE,DESC_ALL, DESC_MOST,DESC_NONE,SQL_CODE_TIMESTAMP,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_CONCISE_TYPE,TEXT("SQL_DESC_CONCISE_TYPE"),FALSE,DESC_ALL,DESC_MOST,DESC_AD,SQL_TYPE_TIMESTAMP,SQL_C_DEFAULT,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_AUTO_UNIQUE_VALUE,TEXT("SQL_DESC_AUTO_UNIQUE_VALUE"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,0,0,sizeof(SQLINTEGER),SQL_IS_INTEGER,
//SQL_DESC_BIND_OUT_OF_LINE,TEXT("SQL_DESC_BIND_OUT_OF_LINE"),FALSE,DESC_AD,DESC_AD,SQL_TRUE,sizeof(SQLSMALLINT),SQL_IS_NOT_POINTER,
SQL_DESC_BASE_COLUMN_NAME,TEXT("SQL_DESC_BASE_COLUMN_NAME"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_CASE_SENSITIVE,TEXT("SQL_DESC_CASE_SENSITIVE"),FALSE,DESC_ID,DESC_NONE,DESC_NONE,0,0,sizeof(SQLINTEGER),SQL_IS_INTEGER,
SQL_DESC_CATALOG_NAME,TEXT("SQL_DESC_CATALOG_NAME"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_DATETIME_INTERVAL_PRECISION, TEXT("SQL_DESC_DATETIME_INTERVAL_PRECISION"),FALSE, DESC_ALL,DESC_MOST,DESC_NONE,3,0,sizeof(SQLINTEGER),SQL_IS_INTEGER,
SQL_DESC_DISPLAY_SIZE,TEXT("SQL_DESC_DISPLAY_SIZE"),FALSE,DESC_IRD,DESC_NONE, DESC_NONE,0,0,sizeof(SQLINTEGER),SQL_IS_INTEGER,
SQL_DESC_FIXED_PREC_SCALE,TEXT("SQL_DESC_FIXED_PREC_SCALE"),FALSE,DESC_ID,DESC_NONE,DESC_NONE,0,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_LABEL,TEXT("SQL_DESC_LABEL"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_LENGTH,TEXT("SQL_DESC_LENGTH"),FALSE,DESC_ALL,DESC_MOST,DESC_NONE,23,0,sizeof(SQLUINTEGER),SQL_IS_UINTEGER,
SQL_DESC_LITERAL_PREFIX,TEXT("SQL_DESC_LITERAL_PREFIX"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_LITERAL_SUFFIX,TEXT("SQL_DESC_LITERAL_SUFFIX"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_LOCAL_TYPE_NAME,TEXT("SQL_DESC_LOCAL_TYPE_NAME"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_NAME,TEXT("SQL_DESC_NAME"),FALSE,DESC_ID,DESC_IPD,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_NULLABLE,TEXT("SQL_DESC_NULLABLE"),FALSE,DESC_ID,DESC_NONE,DESC_NONE,0,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_OCTET_LENGTH,TEXT("SQL_DESC_OCTET_LENGTH"),FALSE,DESC_ALL,DESC_MOST,DESC_NONE,sizeof(tsval),0,sizeof(SQLINTEGER),SQL_IS_INTEGER,
SQL_DESC_OCTET_LENGTH_PTR,TEXT("SQL_DESC_OCTET_LENGTH_PTR"),FALSE,DESC_AD,DESC_AD,DESC_AD,(SQLINTEGER)(&cbtsval),(SQLINTEGER)NULL,sizeof(SQLINTEGER),SQL_IS_POINTER,
SQL_DESC_PARAMETER_TYPE,TEXT("SQL_DESC_PARAMETER_TYPE"),FALSE,DESC_IPD,DESC_IPD,DESC_IPD,SQL_PARAM_OUTPUT,SQL_PARAM_INPUT,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_PRECISION,TEXT("SQL_DESC_PRECISION"),FALSE,DESC_ALL,DESC_MOST,DESC_NONE,23,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_SCALE,TEXT("SQL_DESC_SCALE"),FALSE,DESC_ALL,DESC_MOST,DESC_NONE,3,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_SCHEMA_NAME,TEXT("SQL_DESC_SCHEMA_NAME"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_SEARCHABLE,TEXT("SQL_DESC_SEARCHABLE"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,0,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_TABLE_NAME,TEXT("SQL_DESC_TABLE_NAME"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_TYPE_NAME,TEXT("SQL_DESC_TYPE_NAME"),FALSE,DESC_ID,DESC_NONE,DESC_NONE,(SQLINTEGER)szParamName,0,sizeof(SQLCHAR),0,
SQL_DESC_UNSIGNED,TEXT("SQL_DESC_UNSIGNED"),FALSE,DESC_ID,DESC_NONE,DESC_NONE,0,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_UPDATABLE,TEXT("SQL_DESC_UPDATABLE"),FALSE,DESC_IRD,DESC_NONE,DESC_NONE,0,0,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_INDICATOR_PTR,TEXT("SQL_DESC_INDICATOR_PTR"),FALSE,DESC_AD,DESC_AD,DESC_AD,SQL_NULL_DATA,(SQLINTEGER)NULL,sizeof(SQLPOINTER),SQL_IS_POINTER,
SQL_DESC_UNNAMED,TEXT("SQL_DESC_UNNAMED"),FALSE,DESC_ID,DESC_IPD,DESC_NONE,SQL_UNNAMED,SQL_NAMED,sizeof(SQLSMALLINT),SQL_IS_SMALLINT,
SQL_DESC_DATA_PTR,TEXT("SQL_DESC_DATA_PTR"),FALSE,DESC_AD,DESC_MOST,DESC_AD,(SQLINTEGER)&tsval,(SQLINTEGER)NULL,sizeof(SQLPOINTER),SQL_IS_POINTER
};

struct tagDIAGINFO {
SQLSMALLINT uwDescField;
LPTSTR szDescFieldName;
SWORD irecRecNumber;
} rgDiagInfo[] ={
//SQL_DIAG_CURSOR_ROW_COUNT,TEXT("SQL_DIAG_CURSOR_ROW_COUNT"),0,
//SQL_DIAG_DYNAMIC_FUNCTION,TEXT("SQL_DIAG_DYNAMIC_FUNCTION"),0,
//SQL_DIAG_DYNAMIC_FUNCTION_CODE,TEXT("SQL_DIAG_DYNAMIC_FUNCTION_CODE"),0,
SQL_DIAG_NUMBER,TEXT("SQL_DIAG_NUMBER"),0,
SQL_DIAG_RETURNCODE,TEXT("SQL_DIAG_RETURNCODE"),0,
//SQL_DIAG_ROW_NUMBER,TEXT("SQL_DIAG_ROW_NUMBER"),0,
//SQL_DIAG_COLUMN_NUMBER,TEXT("SQL_DIAG_COLUMN_NUMBER"),1,
//SQL_DIAG_ROW_COUNT,TEXT("SQL_DIAG_ROW_COUNT"),0,
SQL_DIAG_SQLSTATE,TEXT("SQL_DIAG_SQLSTATE"),1,
SQL_DIAG_NATIVE,TEXT("SQL_DIAG_NATIVE"),1,
SQL_DIAG_MESSAGE_TEXT,TEXT("SQL_DIAG_MESSAGE_TEXT"),1,
SQL_DIAG_CLASS_ORIGIN,TEXT("SQL_DIAG_CLASS_ORIGIN"),1,
SQL_DIAG_SUBCLASS_ORIGIN,TEXT("SQL_DIAG_SUBCLASS_ORIGIN"),1,
//SQL_DIAG_CONNECTION_NAME,TEXT("SQL_DIAG_CONNECTION_NAME"),1,
SQL_DIAG_SERVER_NAME,TEXT("SQL_DIAG_SERVER_NAME"),1,
};

BOOLg_f3XDriver=FALSE;

//the insert statement bufferto hold the insert statement
TCHAR szInsertStmt[MAX_STRING_SIZE];

//-----------------------------------------------------------------------
// Function: Supported
//-----------------------------------------------------------------------

_inline UWORD PASCAL Supported(UWORD uwSQLFunc)
{
UWORDfLevel2=FALSE;
RETCODErc=SQL_SUCCESS;

rc = SQLGetFunctions(hdbc, uwSQLFunc,&fLevel2);
RETCHECK(SQL_SUCCESS, rc,szSQLGETFUNCTIONS);

return(fLevel2);

} //Supported()


/*------------------------------------------------------------------------------
*Function:CheckDataResults - To check data returned by Fetch calls
*Checks data at (row, col) in result set against (row, col) in table
* ----------------------------------------------------------------------------*/
BOOL CheckDataResults(SWORD row, SWORD iTableCol, FIELDINFO* pField, DSTRUCT* pDataStr,
SDWORD cbValue, TCHAR* rgbValue)
{
if((pField ->nullable)&& (cbValue<1))
{
if(cbValue == SQL_NULL_DATA && pDataStr[iTableCol].cb == SQL_NULL_DATA)
return TRUE;
return FALSE;
}

return CmpODBCtoCHAR(pDataStr, rgbValue, pField->wSQLType, iTableCol);
}


/*------------------------------------------------------------------------------
/Function:BinToHex
/Purpose:Render a buffer contents as a hexidecimal string.
/*----------------------------------------------------------------------------*/

void PASCAL BinToHex(PTR rgbValue, SWORD cbValue, LPTSTR szHexString)
{
UWORD ib;
LPTSTR pch;
TCHAR rgchDigits[] = TEXT("0123456789ABCDEF");

lstrcpy(szHexString, TEXT("0x"));
if(cbValue > (MAX_STRING_SIZE - 3)/2 || cbValue < 0)
cbValue = (MAX_STRING_SIZE - 3)/2;

pch = &szHexString[lstrlen(szHexString)];
for(ib=0; ib < cbValue; ib++)
{
*pch++ = rgchDigits[((LPBYTE)rgbValue)[ib] >> 4];
*pch++ = rgchDigits[((LPBYTE)rgbValue)[ib] & 0xF];
}
*pch = TEXT('\0');

} /* BinToHex() */



/*-----------------------------------------------------------------------*/
/* Function: SearchForError */
/*-----------------------------------------------------------------------*/

_inline int PASCAL SearchForError(HENV henv, HDBC hdbc,HSTMT hstmt,LPTSTR szFindState)
{
TCHAR buf[MAX_STRING_SIZE];
RETCODE rc;
TCHAR szState[6];
int found = FALSE;

for(rc = SQLError(henv, hdbc, hstmt, szState, NULL, buf, MAX_STRING_SIZE, NULL)
; !found && (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
; rc = SQLError(henv, NULL, NULL, szState, NULL, buf, MAX_STRING_SIZE, NULL))
{
found = lstrcmp(szState, szFindState) == 0;
}

return(found);

} /* SearchForError() */


/*-----------------------------------------------------------------------*/
/* Function: szWrite */
/*-----------------------------------------------------------------------*/

_inline VOID WINAPI szWrite(LPTSTR szStr, BOOL fNewLine)
{
szLogPrintf(lpSI, FALSE, szStr);
if(fNewLine)
szLogPrintf(lpSI, FALSE, TEXT("\r\n"));
}

/*-----------------------------------------------------------------------*/
/* Function: GetErrors */
/*-----------------------------------------------------------------------*/

VOID PASCAL GetErrors(HENV henv,HDBC hdbc,HSTMT hstmt)
{
TCHAR buf[MAX_ERROR_SIZE];
TCHAR largebuf[COMBINED_SIZE];
TCHAR szState[100];
RETCODE rc=SQL_SUCCESS;

for(rc = SQLError(henv, hdbc, hstmt, szState, NULL, buf, MAX_ERROR_SIZE, NULL)
; RC_SUCCESSFUL(rc)
; rc = SQLError(henv, NULL, NULL, szState, NULL, buf, MAX_ERROR_SIZE, NULL))
{
wsprintf(largebuf,TEXT("\t\t\tState: %s"), szState);
szWrite(largebuf, TRUE);
wsprintf(largebuf,TEXT("\t\t\tError: %s"), buf);
szWrite(largebuf, TRUE);
}

} /* GetErrors() */


/*-----------------------------------------------------------------------*/
/* Function: GetDiagRecs */
/*-----------------------------------------------------------------------*/

VOID PASCAL GetDiagRecs(SWORD fHandleType,SQLHANDLE hHandle)
{
TCHARszMessageText[XLARGEBUFF]=TEXT(""),
szBuff[XLARGEBUFF]=TEXT("");
TCHARszSQLState[100]=TEXT("");
RETCODErc=SQL_SUCCESS;
SWORDirecRecNumber=0;
SDWORDfNativeError=0;
SWORDcbBufferLength=sizeof(szMessageText),
cbTextLength=0;
SDWORDsdNumRecs=0;

if (fDiagFieldSupported)
{
/* Get number of diag, records */
rc = SQLGetDiagField(fHandleType, hHandle,0,SQL_DIAG_NUMBER,
&sdNumRecs,sizeof(sdNumRecs),NULL);
}


for(irecRecNumber=1;(irecRecNumber <= sdNumRecs) && RC_SUCCESSFUL(rc);irecRecNumber++)
{
rc = SQLGetDiagRec(fHandleType, hHandle, irecRecNumber,szSQLState,&fNativeError,
szMessageText,cbBufferLength,&cbTextLength);

if (RC_SUCCESSFUL(rc))
{
wsprintf(szBuff,TEXT("\t\t\tState: %s"), szSQLState);
szWrite(szBuff, TRUE);
wsprintf(szBuff,TEXT("\t\t\tError: %s"), szMessageText);
szWrite(szBuff, TRUE);
}

}


} /* GetErrors() */



/*-----------------------------------------------------------------------*/
/* Function: DisplayAllErrors */
/*-----------------------------------------------------------------------*/

void PASCAL DisplayAllErrors()
{
if (!fDiagRecSupported)
{
GetErrors(henv,NULL,NULL);
GetErrors(NULL,hdbc,NULL);
GetErrors(NULL,NULL,hstmt);
}
else
{
GetDiagRecs(SQL_HANDLE_ENV,henv);
GetDiagRecs(SQL_HANDLE_DBC,hdbc);
GetDiagRecs(SQL_HANDLE_STMT,hstmt);
GetDiagRecs(SQL_HANDLE_DESC,hdesc);
}

} /* DisplayAllErrors() */


/*-----------------------------------------------------------------------*/
/* Function: RetcodeToTCHAR */
/*-----------------------------------------------------------------------*/

LPTSTR PASCAL RetcodeToTCHAR(RETCODE retcode, LPTSTR buf)
{
switch (retcode)
{
case SQL_SUCCESS:
lstrcpy (buf,TEXT("SQL_SUCCESS"));
break;
case SQL_ERROR:
lstrcpy (buf,TEXT("SQL_ERROR"));
break;
case SQL_SUCCESS_WITH_INFO:
lstrcpy (buf,TEXT("SQL_SUCCESS_WITH_INFO"));
break;
case SQL_NO_DATA_FOUND:
lstrcpy (buf,TEXT("SQL_NO_DATA_FOUND"));
break;
case SQL_NEED_DATA:
lstrcpy (buf,TEXT("SQL_NEED_DATA"));
break;
case SQL_INVALID_HANDLE:
lstrcpy (buf,TEXT("SQL_INVALID_HANDLE"));
break;
case SQL_STILL_EXECUTING:
lstrcpy (buf,TEXT("SQL_STILL_EXECUTING"));
break;
default:
lstrcpy(buf,TEXT("UNKNOWN rc"));
}

return buf;
}



//-----------------------------------------------------------------------
// Function: ReturnCheck
//-----------------------------------------------------------------------

int PASCAL ReturnCheck(RETCODE retExpected, RETCODE retReceived, LPTSTR szFunction,
LPTSTR szFile, int iLine,BOOL fDiag)
{
TCHAR buf[MAX_STRING_SIZE];

if(retExpected == retReceived)
return TRUE;

szWrite(TEXT(""), TRUE);

szWrite(TEXT("\t\t\t"), FALSE);
szWrite(szFunction, TRUE);

szWrite(TEXT("\t\t\tExpected: "), FALSE);
szWrite(RetcodeToTCHAR(retExpected, buf), TRUE);

szWrite(TEXT("\t\t\tReceived: "), FALSE);
szWrite(RetcodeToTCHAR(retReceived, buf), TRUE);

if (!fDiag)
DisplayAllErrors();

wsprintf(buf,TEXT("\t\t\t%s: %d"), szFile, iLine);
szWrite(buf, TRUE);

szWrite(TEXT("\t\t\t -------- "), TRUE);

lpSI->failed++;

return FALSE;
}

//-----------------------------------------------------------------------
// Function: ErrsHandle
//-----------------------------------------------------------------------

int PASCAL ErrsHandle(SWORD fHandleType,SQLHANDLE hHandle, RETCODE retExpected,
RETCODE retReceived, LPTSTR szFunction, LPTSTR szFile, int iLine,BOOL fDiag)
{
TCHAR buf[MAX_STRING_SIZE];

if(retExpected == retReceived)
return TRUE;

szWrite(TEXT(""), TRUE);

szWrite(TEXT("\t\t\t"), FALSE);
szWrite(szFunction, TRUE);

szWrite(TEXT("\t\t\tExpected: "), FALSE);
szWrite(RetcodeToTCHAR(retExpected, buf), TRUE);

szWrite(TEXT("\t\t\tReceived: "), FALSE);
szWrite(RetcodeToTCHAR(retReceived, buf), TRUE);

if (!fDiag)
GetDiagRecs(fHandleType,hHandle);

wsprintf(buf,TEXT("\t\t\t%s: %d"), szFile, iLine);
szWrite(buf, TRUE);

szWrite(TEXT("\t\t\t -------- "), TRUE);

lpSI->failed++;

return FALSE;
}

/*------------------------------------------------------------------------------
/Function:FindError
/Purpose:Find the expected error message. Display a failure if not found.
/*----------------------------------------------------------------------------*/

BOOL PASCAL FindError(SWORD fHandleType, LPTSTR szFindState)
{
SQLHANDLE handle = SQL_NULL_HANDLE;
SWORD irec;
TCHAR szErrorMsg[SQL_MAX_MESSAGE_LENGTH];
RETCODE rc;
TCHAR szState[6];

switch(fHandleType)
{
case SQL_HANDLE_ENV:
handle = henv;
break;

case SQL_HANDLE_DBC:
handle = hdbc;
break;

case SQL_HANDLE_STMT:
handle = hstmt;
break;

case SQL_HANDLE_DESC:
if(!Supported(SQL_API_SQLGETDIAGREC))
return TRUE;

handle = hdesc;
break;

default:
DISPLAYERROR(TEXT("FindError"), TEXT("invalid fHandleType"));
return FALSE;
}

for(irec = 1
; rc = SQLGetDiagRec(fHandleType, handle, irec, szState, NULL, szErrorMsg, SQL_MAX_MESSAGE_LENGTH, NULL)
, RC_SUCCESSFUL(rc)
; irec++)
{
if(lstrcmp(szState, szFindState) == 0)
return TRUE;
}
return FALSE;

} /* FindError() */

/*------------------------------------------------------------------------------
*Function:CompareWithExpected
*Checks data in rgbValue against expected value given row, col, and rgFields.
*Posts an error if mismatch
* ----------------------------------------------------------------------------*/
void CompareWithExpected(PTR rgbValue, SDWORD * pcbValue, UWORD row, UWORD icol, FIELDINFO *rgFields)
{
LPTSTR pch=NULL;
TCHAR szExpBuf[MAX_STRING_SIZE]=TEXT("");
TCHAR * stopstr;

pch = qtMakeData(row-1, icol-1, &rgFields[icol-1], szExpBuf);

// Field is not READ ONLY, compare the value we inserted
if(pch)
{
// Where row and column match and field is nullable, a NULL exists
if (icol-1 == row-1 && rgFields[icol-1].nullable)
{
// Post an error if not SQL_NULL_DATA
if (*pcbValue != SQL_NULL_DATA)
DISPLAYERROR(TEXT("Simulate BindCol"),TEXT("Didn't return SQL_NULL_DATA"));
}
else
{
BOOL bCompare;
TCHAR * pstr=szExpBuf;// Pointer to start of expected string

switch(rgFields[icol-1].wSQLType)
{
case SQL_BIT:
case SQL_TINYINT:
case SQL_SMALLINT:
case SQL_INTEGER:
// Compare the data values as long // tcsncmp
bCompare=(_ttol(rgbValue) == _ttol(pstr));
break;
case SQL_NUMERIC:
case SQL_DECIMAL:
case SQL_FLOAT:
case SQL_REAL:
case SQL_DOUBLE:
// Compare the data values as doubles strtod
bCompare=(_tcstod(rgbValue, &stopstr) == _tcstod(pstr, &stopstr));
break;
case SQL_DATE:
case SQL_TIME:
case SQL_TIMESTAMP:
case SQL_TYPE_DATE:
case SQL_TYPE_TIME:
case SQL_TYPE_TIMESTAMP:
pstr=szExpBuf+lstrlen(TEXT("{ts '"));
// Replace second ' with '\0''
_tcstok(pstr, TEXT("'"));
if (lstrlen(rgbValue) > lstrlen(TEXT("1994-10-10 10:00:00")))
lstrcat(pstr, TEXT(".000"));
bCompare=(!_tcsncmp(rgbValue, pstr, rgFields[icol-1].precision));

break; 
default:
bCompare=(!_tcsncmp(rgbValue, pstr, lstrlen(pstr)));
}

// Compare the data values as char
if (!bCompare)
{
DISPLAYERROR(TEXT("Simulate BindCol"),TEXT("Returned data didn't match"));
szWrite(TEXT("\t\t\tExpected: "), FALSE);
szWrite(pstr, TRUE);
szWrite(TEXT("\t\t\tReceived: "), FALSE);
szWrite(rgbValue, TRUE);
}
}
}
} //CompareWithExpected

//-----------------------------------------------------------------------
// Function: ResetHstmt
//-----------------------------------------------------------------------
void ResetHstmt(SQLHSTMT * phstmt)
{
SQLRETURN rc;

// Free and reallocate stmt handle to clean up
rc=SQLFreeHandle(SQL_HANDLE_STMT, *phstmt);
ERRSHANDLE(SQL_HANDLE_STMT, *phstmt, SQL_SUCCESS, rc, szSQLFREESTMT);
rc=SQLAllocHandle(SQL_HANDLE_STMT, hdbc, phstmt);
RETCHECK(SQL_SUCCESS, rc,szSQLALLOCHANDLE);
}

/*-----------------------------------------------------------------------*/
/* Function: GetSomeData */
/*-----------------------------------------------------------------------*/

VOID PASCAL GetSomeData(HSTMT lhstmt, LPTSTR szTableName,BOOL fBindCol,
FIELDINFO rgField)
{
RETCODErc=SQL_SUCCESS;
SDWORDcbValue=0;
TCHARszQuery[XLARGEBUFF]=TEXT(""),
szBuff[XLARGEBUFF]=TEXT("");
TCHARszErrMsg[MAX_STRING_SIZE]=TEXT(""),
szDataItem[XLARGEBUFF]=TEXT("");
LPTSTR pch=NULL;

pch = qtMakeData(0, 0,&rgField, szDataItem);

wsprintf(szQuery,szSELECTSTAR,szTableName);
rc=SQLExecDirect(lhstmt,szQuery,SQL_NTS);
//RETCHECK(SQL_SUCCESS,rc,szSQLEXECDIRECT);

if (fBindCol)
{
rc = SQLBindCol(lhstmt, 1, SQL_C_TCHAR, szBuff, sizeof(szBuff),&cbValue);
//RETCHECK(SQL_SUCCESS, rc, szSQLBINDCOL);
}

rc = SQLFetch(lhstmt);
//RETCHECK(SQL_SUCCESS, rc,szSQLFETCH);

if (!fBindCol)
{
rc = SQLGetData(lhstmt,1, SQL_C_TCHAR,szBuff, sizeof(szBuff), NULL);
//RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);
}

lstrcmp(szDataItem,szBuff);

} /* GetSomeData() */



/*-----------------------------------------------------------------------*/
/* Function: SelectFromTable */
/*-----------------------------------------------------------------------*/

_inline RETCODE PASCAL SelectFromTable(QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
TCHARszQuery[LARGEBUFF];

wsprintf(szQuery,TEXT("select %s from %s"),
//wsprintf(szQuery,TEXT("select %s from %s order by %s"),
lpqt->szColNames, lpqt->szTableName/*, lpqt->szColNames*/);

rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

return(rc);

} /* SelectFromTable() */

/*-----------------------------------------------------------------------*/
/* Function: SelectFromTableFetch */
/*-----------------------------------------------------------------------*/

_inline RETCODE PASCAL SelectFromTableFetch(QTSTRUCT *lpqt, TCHAR* szColName)
{
RETCODErc=SQL_SUCCESS;
TCHARszQuery[LARGEBUFF];

wsprintf(szQuery,TEXT("select %s from %s order by %s"),
lpqt->szColNames, lpqt->szTableName, szColName);

rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
if((SQL_SUCCESS!=rc) && (SQL_SUCCESS_WITH_INFO!=rc)){
RETCHECK(SQL_SUCCESS_WITH_INFO, rc,szSQLEXECDIRECT);
return SQL_NO_DATA;
}

return SQL_SUCCESS;
} /* SelectFromTableFetch() */

//-----------------------------------------------------------------------
// Function: AllocHstmt
//-----------------------------------------------------------------------

_inline BOOL PASCAL AllocHstmt()
{
BOOLfFailed=FALSE;
RETCODErc=SQL_SUCCESS;

if (Supported(SQL_API_SQLALLOCHANDLE))
{
rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc, &hstmt);
if(!RETCHECK(SQL_SUCCESS, rc, szSQLALLOCHANDLE))
fFailed=TRUE;
}
else
{
rc = SQLAllocStmt(hdbc, &hstmt);
if(!RETCHECK(SQL_SUCCESS, rc, szSQLALLOCSTMT))
fFailed=TRUE;
}


return(fFailed);

} //AllocHstmt()



//-----------------------------------------------------------------------
// Function: AllocHdbc
//-----------------------------------------------------------------------

_inline BOOL PASCAL AllocHdbc(HDBC *phdbc)
{
BOOLfFailed=FALSE;
RETCODErc=SQL_SUCCESS;

if (Supported(SQL_API_SQLALLOCHANDLE))
{
rc = SQLAllocHandle(SQL_HANDLE_DBC,henv, phdbc);
if(!RETCHECK(SQL_SUCCESS, rc, szSQLALLOCHANDLE))
fFailed=TRUE;
}
else
{
rc = SQLAllocConnect(henv, phdbc);
if(!RETCHECK(SQL_SUCCESS, rc, szSQLALLOCCONNECT))
fFailed=TRUE;
}


return(fFailed);

} //AllocHdbc()


//-----------------------------------------------------------------------
// Function:AllocHenv
//-----------------------------------------------------------------------

_inline BOOL PASCAL PASCAL AllocHenv()
{
BOOLfFailed=FALSE;
RETCODErc=SQL_SUCCESS;

rc = SQLAllocEnv(&henv);
if (!RETCHECK(SQL_SUCCESS, rc,szSQLALLOCENV))
fFailed=TRUE;

return(fFailed);

} //AllocHenv()

//-----------------------------------------------------------------------
// Function: SetStatementAttributes
//-----------------------------------------------------------------------

_inline BOOL PASCAL SetStatementAttributes(UWORD fAttr, PTR rgbValue)
{
RETCODErc=SQL_SUCCESS;
TCHARszAPI[MEDBUFF];
BOOLfFailed=FALSE;

if (Supported(SQL_API_SQLSETSTMTATTR))
{
rc = SQLSetStmtAttr(hstmt,(SDWORD)fAttr,rgbValue,sizeof(UDWORD));
lstrcpy(szAPI,szSQLSETSTMTATTR);
}
else
{
rc = SQLSetStmtOption(hstmt,fAttr,(UDWORD)rgbValue);
lstrcpy(szAPI,szSQLSETSTMTOPTION);
}

if (rc != SQL_SUCCESS)
{
if(!FindError(SQL_HANDLE_STMT,szHYC00))
RETCHECK(SQL_SUCCESS, rc,szAPI);

fFailed=TRUE;
}

return(fFailed);

} //SetStatementAttributes()



//-----------------------------------------------------------------------
// Function: SetConnectionAttributes
//-----------------------------------------------------------------------

_inline BOOL PASCAL SetConnectionAttributes(HDBC lhdbc,UWORD fAttr, UDWORD rgbValue)
{
RETCODErc=SQL_SUCCESS;
TCHARszAPI[MEDBUFF];
BOOLfFailed=FALSE;

if (Supported(SQL_API_SQLSETCONNECTATTR))
{
rc = SQLSetConnectAttr(lhdbc,(SDWORD)fAttr,(PTR)rgbValue,sizeof(UDWORD));
lstrcpy(szAPI,szSQLSETCONNECTATTR);
}
else
{
rc = SQLSetConnectOption(lhdbc,fAttr,rgbValue);
lstrcpy(szAPI,szSQLSETCONNECTOPTION);
}

if (rc != SQL_SUCCESS)
{
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS, rc,szAPI);

fFailed=TRUE;
}

return(fFailed);

} //SetConnectionAttributes()



//-----------------------------------------------------------------------
// Function: FreeStmt
//-----------------------------------------------------------------------

_inline BOOL PASCAL FreeStmt(UWORD fOption)
{
RETCODErc=SQL_SUCCESS;

if (Supported(SQL_API_SQLFREEHANDLE) && (fOption == SQL_DROP))
{
rc = SQLFreeHandle(SQL_HANDLE_STMT,hstmt);
RETCHECK(SQL_SUCCESS, rc,szSQLFREEHANDLE);
}
else
{
rc = SQLFreeStmt(hstmt, fOption);
RETCHECK(SQL_SUCCESS, rc,szSQLFREESTMT);
}

return(RC_NOTSUCCESSFUL(rc));

} //FreeStmt()

//-----------------------------------------------------------------------
// Function: DropHdbc
//-----------------------------------------------------------------------

VOID PASCAL DropHdbc(HDBC hdbc)
{
RETCODE rc=SQL_SUCCESS;

if (Supported(SQL_API_SQLFREEHANDLE))
{
rc = SQLFreeHandle(SQL_HANDLE_DBC,hdbc);
RETCHECK(SQL_SUCCESS, rc,szSQLFREEHANDLE);
}
else
{
rc = SQLFreeConnect(hdbc);
RETCHECK(SQL_SUCCESS, rc,szSQLFREECONNECT);
}

} //DropHdbc()



//-----------------------------------------------------------------------
// Function: DropHenv
//-----------------------------------------------------------------------

_inline VOID PASCAL DropHenv()
{
RETCODE rc=SQL_SUCCESS;

if (Supported(SQL_API_SQLFREEHANDLE))
{
rc = SQLFreeHandle(SQL_HANDLE_ENV,henv);
RETCHECK(SQL_SUCCESS, rc,szSQLFREEHANDLE);
}
else
{
rc = SQLFreeEnv(henv);
RETCHECK(SQL_SUCCESS, rc,szSQLFREEENV);
}

} //DropHenv()


//-----------------------------------------------------------------------
// Function: ClearErrorQueue
//-----------------------------------------------------------------------

VOID PASCAL ClearErrorQueue()
{
TCHAR szErrMsg[MAX_STRING_SIZE];
TCHAR szState[6];

/* Eat up the SQL_SUCCESS_WITH_INFO error record */
while (RC_SUCCESSFUL(SQLError(NULL, hdbc,NULL, szState, NULL,
szErrMsg, sizeof(szErrMsg), NULL)))
;
}


//-----------------------------------------------------------------------
// Function: Is3XDriver
//-----------------------------------------------------------------------

BOOL PASCAL Is3XDriver(HDBC hdbc,QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
LPTSTRpch=NULL;
BOOLf3XDriver=FALSE;

rc = SQLGetInfo(hdbc, SQL_DRIVER_ODBC_VER, lpqt->buf, MAX_STRING_SIZE, NULL);
if(RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO))
{
pch = _tcstok(lpqt->buf,TEXT("."));

if (!_tcsnccmp(pch,TEXT("02"), 2))
{
pch = _tcstok(NULL,TEXT("."));

if(!_tcsnccmp(pch,TEXT("00"), 2))
DISPLAYERROR(szSQLGETINFO,TEXT("Driver returned ODBC version 2.00 which is no longer supported. Please upgrade to 2.01"));

}
else if (!_tcsnccmp(pch,TEXT("03"), 2))
f3XDriver=TRUE;
}

uDriverODBCVer=_ttoi(lpqt->buf);

return(f3XDriver);

} //Is3XDriver()


//-----------------------------------------------------------------------
// Function: Connect
//-----------------------------------------------------------------------

BOOL PASCAL Connect(lpSERVERINFO lpSI,QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
SDWORD sdwAppVer;

/* The signal for test to use it's
own allocated handles, is if the a server name is passed in. If it
is not, the test should use the hdbc passed in */

/* ****************** Allocate Env ************************* */
if (AllocHenv())
goto ConnectErrorRet;

/* ****************** Set Application Version ************************* */
rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,
SQL_IS_UINTEGER);
if(!RETCHECK(SQL_SUCCESS, rc, szSQLSETENVATTR))
goto ConnectErrorRet;

/* ****************** Allocate Connection ************************* */

rc = SQLAllocConnect(henv, &hdbc);
if(!RETCHECK(SQL_SUCCESS, rc, szSQLALLOCCONNECT))
goto ConnectErrorRet;

/* ****************** Set Connection Attributes ************************* */
rc = SQLSetConnectOption(hdbc,SQL_ODBC_CURSORS,lpSI->vCursorLib);

if(!FindError(SQL_HANDLE_DBC,szHYC00))
if (!RETCHECK(SQL_SUCCESS, rc,szSQLSETCONNECTOPTION))
goto ConnectErrorRet;

/* create a DriverConnect string */
wsprintf(lpqt->sz, TEXT("dsn=%s;uid=%s;pwd=%s;"),
lpSI->szValidServer0,
lpSI->szValidLogin0,
lpSI->szValidPassword0);

/* since SQL_DRIVER_COMPLETE is used, it is possible that the
driver will bring up a dialog asking for more information. */
rc = SQLDriverConnect(hdbc, lpSI->hwnd, lpqt->sz,
SQL_NTS, lpqt->buf, MAX_STRING_SIZE, NULL, SQL_DRIVER_COMPLETE);

if (RC_NOTSUCCESSFUL(rc))
{
if (!RETCHECK(SQL_SUCCESS, rc,szSQLDRIVERCONNECT))
goto ConnectErrorRet;
}
else
ClearErrorQueue();


rc = SQLDisconnect(hdbc);
RETCHECK(SQL_SUCCESS, rc,szSQLDISCONNECT);

rc = SQLConnect(hdbc, lpSI->szValidServer0, SQL_NTS,
lpSI->szValidLogin0, SQL_NTS,
lpSI->szValidPassword0, SQL_NTS);

if (RC_SUCCESSFUL(rc))
ClearErrorQueue();
else if (!RETCHECK(SQL_SUCCESS, rc,szSQLCONNECT))
goto ConnectErrorRet;

// Set the global 3.0 driver flag
g_f3XDriver=Is3XDriver(hdbc,lpqt);

// We're now connected, check application version and adjust expected error
// states appropriately.
rc = SQLGetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, &sdwAppVer, SQL_IS_UINTEGER, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETENVATTR);

if (RC_SUCCESSFUL(rc) && (SQL_OV_ODBC2 == sdwAppVer))
{
lstrcpy(szHY009, TEXT("S1009"));
lstrcpy(szHYC00, TEXT("S1C00"));
}

return(FALSE);

ConnectErrorRet:

return(TRUE);

} //Connect()


//-----------------------------------------------------------------------
// Function: IgnoreType
//-----------------------------------------------------------------------

BOOL PASCAL IgnoreType(LPTSTR szType,SWORD wSQLType)
{
BOOL fIgnore=FALSE;

/* If a line below is compiled in, that type will not be used in creating
the table. (useful for when one specific type is causing problems in
a particular test, this way the test can be run for all other types)
*/

if (!lstrcmp(szType,TEXT("LONG RAW")) ||
!lstrcmp(szType,TEXT("LONG VARTCHAR")) ||
!lstrcmp(szType,TEXT("LONG VARTCHAR FOR BIT DATA")))
{
fIgnore=TRUE;
}

//if (wSQLType != SQL_CHAR)
//fIgnore=TRUE;

return(fIgnore);

} //IgnoreType()


//-----------------------------------------------------------------------
// Function: CheckConformanceLevel
//-----------------------------------------------------------------------

UWORD PASCAL CheckConformanceLevel()
{
RETCODE rc=SQL_SUCCESS;
UWORDfIndex=0;
SWORDcb=0;

rc = SQLGetInfo(hdbc, SQL_ODBC_SQL_CONFORMANCE, &fIndex,
sizeof (fIndex), &cb);
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

if(fIndex != 0 && fIndex != 1 && fIndex != 2) {
/* one of these two values should always be returned */
DISPLAYERROR(szSQLGETINFO,TEXT("SQL_ODBC_API_CONFORMANCE - invalid value"));
}

return(fIndex);

} //CheckConformanceLevel()

/*-----------------------------------------------------------------------*/
/* Function: TestConnectionOptions */
/*-----------------------------------------------------------------------*/

void PASCAL TestConnectionOptions()
{
RETCODErc=SQL_SUCCESS;
SDWORDsdw,
cbValue=0;
BOOLfConnectOption=TRUE;
TCHARszAPI[MEDBUFF];
HDBClhdbc=NULL;


rc=SQLAllocConnect(henv,&lhdbc);
RETCHECK(SQL_SUCCESS, rc,szSQLALLOCCONNECT);

SetConnectionAttributes(lhdbc,SQL_ACCESS_MODE,SQL_MODE_READ_WRITE);

SetConnectionAttributes(lhdbc,SQL_AUTOCOMMIT,TRUE);

if (SetConnectionAttributes(lhdbc,SQL_LOGIN_TIMEOUT,TEST_CONNECT_OPTION))
fConnectOption = FALSE;

if (Supported(SQL_API_SQLGETCONNECTATTR))
{
rc = SQLGetConnectAttr(lhdbc, SQL_LOGIN_TIMEOUT, (PTR)&sdw,0,&cbValue);
lstrcpy(szAPI,szSQLGETCONNECTATTR);
}
else
{
rc = SQLGetConnectOption(lhdbc, SQL_LOGIN_TIMEOUT, &sdw);
lstrcpy(szAPI,szSQLSETCONNECTOPTION);
}


if(rc != SQL_SUCCESS)
{
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS, rc,szAPI);
}
else
{
if(sdw != TEST_CONNECT_OPTION && fConnectOption)
DISPLAYERROR(szAPI,TEXT("SQL_LOGIN_TIMEOUT returned incorrect value"));
}

rc=SQLFreeConnect(lhdbc);
RETCHECK(SQL_SUCCESS, rc,szSQLFREECONNECT);

} //TestConnectionOptions()



/*-----------------------------------------------------------------------*/
/* Function: CanAllocHdesc */
/*-----------------------------------------------------------------------*/

BOOL PASCAL CanAllocHdesc()
{
BOOL fSupported=FALSE;

/* Allocate an hdesc: */
if(Supported(SQL_API_SQLALLOCHANDLE))
fSupported = RC_SUCCESSFUL(SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc));

return(fSupported);

}


/*-----------------------------------------------------------------------*/
/* Function: SetErrorCondition */
/*-----------------------------------------------------------------------*/

VOID PASCAL SetErrorCondition(SWORD fHandleType)
{
RETCODE rc=SQL_SUCCESS;

/* The following are used to produce "(S1)HY009" SQL State */
switch (fHandleType)
{
case SQL_HANDLE_ENV:
rc = SQLAllocConnect(henv, NULL);
break;
case SQL_HANDLE_DBC:
rc = SQLAllocStmt(hdbc, NULL);
break;
case SQL_HANDLE_STMT:
rc = SQLExecDirect(hstmt, NULL, SQL_NTS);// Produces a DM error
break;
case SQL_HANDLE_DESC:
rc = SQLGetDescField(hdesc, 0, SQL_DESC_AUTO_UNIQUE_VALUE, NULL, 0, NULL);
break;
}

} //SetErrorCondition()



VOID PASCAL CheckData(SWORD fHandleType,UWORD uwDescField, PTR rgbValue,LPTSTR szDataSourceName)
{
TCHARszBuff[MEDBUFF]=TEXT("");
LPTSTRpszExpState=szHY009;

if (fHandleType == SQL_HANDLE_DESC)
pszExpState=szHY091;

switch(uwDescField)
{
case SQL_DIAG_RETURNCODE:
{
RETCODE rcRec=*(RETCODE *)rgbValue;

if (rcRec!= SQL_ERROR)
{
wsprintf(szBuff,TEXT("\t\t\tSQL_DIAG_RETURN_CODE\tReceived: %d\tExpected: %d"),rcRec,-1);
}
}
break;

case SQL_DIAG_SQLSTATE:
{
if (lstrcmp((LPTSTR)rgbValue,pszExpState))
{
wsprintf(szBuff,TEXT("\t\t\tSQL_DIAG_SQLSTATE\tReceived: %s\tExpected: %s"),(LPTSTR)rgbValue,pszExpState);
}
}
break;
case SQL_DIAG_CLASS_ORIGIN:
{
if (lstrcmp((LPTSTR)rgbValue,szISO))
wsprintf(szBuff,TEXT("\t\t\tSQL_DIAG_CLASS_ORIGIN\tReceived: %s\tExpected: %s"),(LPTSTR)rgbValue,szISO);
}
break;
case SQL_DIAG_SUBCLASS_ORIGIN:
{
if (lstrcmp((LPTSTR)rgbValue,szISO))
wsprintf(szBuff,TEXT("\t\t\tSQL_DIAG_CLASS_ORIGIN\tReceived: %s\tExpected: %s"),(LPTSTR)rgbValue,szISO);
}
break;
case SQL_DIAG_NUMBER:
{
SDWORD cRecs=*(SDWORD *)rgbValue;
if (cRecs != 1)
wsprintf(szBuff,TEXT("\t\t\tSQL_DIAG_NUMBER\tReceived: %d\tExpected: %d"),cRecs,1);
}
break;
case SQL_DIAG_SERVER_NAME:
{
switch(fHandleType)
{
case SQL_HANDLE_DBC:
case SQL_HANDLE_STMT:
if (lstrcmp((LPTSTR)rgbValue,szDataSourceName))
wsprintf(szBuff,TEXT("\t\t\tSQL_DIAG_SERVER_NAME\tReceived: %s\tExpected: %s"),(LPTSTR)rgbValue,szDataSourceName);
break;
}
}
break;
default:
break;

}

if (*szBuff)
DISPLAYERROR(szSQLGETDIAGREC,szBuff);

} /* CheckData() */


/*------------------------------------------------------------------------------
/Function:TestDiagRec
/Purpose:For each type of handle, generate an error. If SQLGetDiagField
/is not supported, verify the appropriate DM error is returned.
/Otherwise, verify SQLGetDiagField returns the expected error
/information. If SQLGetDiagRec is not supported, verify the
/appropriate DM error is returned. Otherwise, verify
/SQLGetDiagField returns the expected error information. If
/SQLGetDiagRec is supported, compare its output with that of
/SQLGetDiagField.
/*----------------------------------------------------------------------------*/

void PASCAL TestDiagRec()
{
SQLRETURNrcFunction,
rc=SQL_SUCCESS;
SWORDcbErrorMsg;
SDWORDfNativeError;
UWORDiHandle=0,
iField=0,
cHandles;

SDWORD*pfNativeError = NULL;
LPTSTRpchMessageText = NULL;

TCHARszDataSourceName[MAX_STRING_SIZE] = TEXT("");
TCHARszState[SQL_SQLSTATE_SIZE+1];
TCHARszErrorMsg[SQL_MAX_MESSAGE_LENGTH];
TCHARszAPI[MEDBUFF]=TEXT("");
TCHARszBuff[MEDBUFF]=TEXT("");

LPTSTRpszExpState=szHY009;
RETCODErcExp=SQL_SUCCESS;

HANDLES rgHandles[] =
{
SQL_HANDLE_ENV, henv,
SQL_HANDLE_DBC, hdbc,
SQL_HANDLE_STMT, hstmt,
SQL_HANDLE_DESC, hdesc,
};

cHandles=sizeof(rgHandles)/sizeof(HANDLES);

/* Call SQLGetInfo to get values to compare: */
rcFunction = SQLGetInfo(hdbc, SQL_DATA_SOURCE_NAME, szDataSourceName, MAX_STRING_SIZE, NULL);
RETCHECK(SQL_SUCCESS, rcFunction, szSQLGETINFO);

/* Allocate an hdesc: */
if(Supported(SQL_API_SQLALLOCHANDLE) && g_f3XDriver)
{
rcFunction = SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc);
RETCHECK(SQL_SUCCESS, rcFunction, szSQLALLOCHANDLE);

// Update the array entry for the hdesc
rgHandles[3].hHandle=hdesc;

}
else
cHandles--;

for (iHandle=0; iHandle < cHandles; iHandle++)
{

SetErrorCondition(rgHandles[iHandle].fHandleType);

rc = SQLGetDiagRec(rgHandles[iHandle].fHandleType, rgHandles[iHandle].hHandle,
1,szState,&fNativeError, szErrorMsg,
SQL_MAX_MESSAGE_LENGTH, &cbErrorMsg);

if (rgHandles[iHandle].fHandleType == SQL_HANDLE_DESC)
pszExpState=szHY091;

rcExp=SQL_SUCCESS;

if(fDiagRecSupported)
{

DIAGRETCHECK(rcExp, rc, szSQLGETDIAGREC);
if (RC_SUCCESSFUL(rc) && lstrcmp(szState,pszExpState))
{
wsprintf(szBuff,TEXT("\t\t\tReceived: %s\tExpected: %s"),szState,pszExpState);
DISPLAYERROR(szSQLGETDIAGREC,szBuff);
}
}
else
{
if(!FindError(rgHandles[iHandle].fHandleType, szIM001))
DISPLAYERROR(szSQLGETDIAGREC, szNotSupported);
}

}


/* Free the hdesc: */
if (hdesc)
{
rcFunction = SQLFreeHandle(SQL_HANDLE_DESC, hdesc);
RETCHECK(rcExp, rcFunction, szSQLFREEHANDLE);
hdesc = SQL_NULL_HDESC;
}

//ExitTestDiagRec

FreeStmt(SQL_CLOSE);

} /* TestDiagRec() */


/*------------------------------------------------------------------------------
/Function:TestDiagField
/Purpose:For each type of handle, generate an error. If SQLGetDiagField
/is not supported, verify the appropriate DM error is returned.
/Otherwise, verify SQLGetDiagField returns the expected error
/information. If SQLGetDiagRec is not supported, verify the
/appropriate DM error is returned. Otherwise, verify
/SQLGetDiagField returns the expected error information. If
/SQLGetDiagRec is supported, compare its output with that of
/SQLGetDiagField.
/*----------------------------------------------------------------------------*/

void PASCAL TestDiagField()
{
SQLRETURNrcFunction,
rc=SQL_SUCCESS;
SWORDcbValue=0;
UWORDiHandle=0,
iField=0,
cHandles;

TCHARszDataSourceName[MAX_STRING_SIZE] = TEXT("");
TCHARrgbValue[SQL_MAX_MESSAGE_LENGTH];
TCHARszAPI[MEDBUFF]=TEXT("");
TCHARszBuff[MEDBUFF]=TEXT("");
RETCODErcExp=SQL_SUCCESS;

HANDLES rgHandles[] =
{
SQL_HANDLE_ENV, henv,
SQL_HANDLE_DBC, hdbc,
SQL_HANDLE_STMT, hstmt,
SQL_HANDLE_DESC, hdesc,
};

cHandles=sizeof(rgHandles)/sizeof(HANDLES);

/* Call SQLGetInfo to get values to compare: */
rcFunction = SQLGetInfo(hdbc, SQL_DATA_SOURCE_NAME, szDataSourceName, MAX_STRING_SIZE, NULL);
RETCHECK(SQL_SUCCESS, rcFunction, szSQLGETINFO);

hdesc = SQL_NULL_HDESC;

/* Allocate an hdesc: */
if(Supported(SQL_API_SQLALLOCHANDLE) && g_f3XDriver)
{
rcFunction = SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc);
RETCHECK(SQL_SUCCESS, rcFunction, szSQLALLOCHANDLE);

// Update the array entry for the hdesc
rgHandles[3].hHandle=hdesc;

}
else
cHandles--;

for (iHandle=0; iHandle < cHandles; iHandle++)
{

SetErrorCondition(rgHandles[iHandle].fHandleType);

if(fDiagFieldSupported)
{
for (iField=0;iField < sizeof(rgDiagInfo)/sizeof(struct tagDIAGINFO);iField++)
{
rc = SQLGetDiagField(rgHandles[iHandle].fHandleType, rgHandles[iHandle].hHandle,
rgDiagInfo[iField].irecRecNumber, rgDiagInfo[iField].uwDescField,
rgbValue, SQL_MAX_MESSAGE_LENGTH, &cbValue);

DIAGRETCHECK(rcExp, rc, szSQLGETDIAGFIELD);

if (RC_SUCCESSFUL(rc))
CheckData(rgHandles[iHandle].fHandleType,rgDiagInfo[iField].uwDescField,
rgbValue,szDataSourceName);
}

}
else
{
rc = SQLGetDiagField(SQL_HANDLE_ENV, henv, 0, SQL_DIAG_RETURNCODE, rgbValue,SQL_MAX_MESSAGE_LENGTH, &cbValue);

if(!FindError(rgHandles[iHandle].fHandleType, szIM001))
DISPLAYERROR(szAPI, szNotSupported);

}

}


/* Free the hdesc: */
if (hdesc)
{
rcFunction = SQLFreeHandle(SQL_HANDLE_DESC, hdesc);
RETCHECK(rcExp, rcFunction, szSQLFREEHANDLE);
hdesc = SQL_NULL_HDESC;
}

//ExitTestDiagField

FreeStmt(SQL_CLOSE);

} /* TestDiagField() */

//------------------------------------------------------------------------------
//Function:CompareAnsiToUnicode
//Compares an ANSI string to a Unicode string
//------------------------------------------------------------------------------
BOOL CompareAnsiToUnicode(SQLCHAR * aszBuf, SQLINTEGER cbA, SQLWCHAR * wszBuf,
SQLINTEGER cbW, SQLTCHAR * szAPI)
{
SQLCHAR aszConvBuf[MAX_STRING_SIZE];
SQLINTEGER cbT;
BOOL bCompare=TRUE;

// Check ANSI count of bytes returned
if ((cbA != SQL_NULL_DATA) && (SQLINTEGER)strlen(aszBuf) != cbA)
{
wsprintf(buf, TEXT("ANSI count of bytes returned incorrect.")\
TEXT(" Expected: %d, received: %d"),
strlen(aszBuf), cbA);
DISPLAYERROR(szAPI, buf);
bCompare=FALSE;
}

// Compare ANSI count of bytes with Unicode count
cbT = (cbA > 0) ? (SQLINTEGER)(cbA * sizeof(SQLWCHAR)) : cbA;
if (cbW != cbT)
{
wsprintf(buf, TEXT("Unicode count of bytes returned incorrect.")\
TEXT(" Expected: %d, received: %d"), cbT, cbW);
DISPLAYERROR(szAPI, buf);
bCompare=FALSE;
}

// Compare strings if not NULL DATA
if (cbA != SQL_NULL_DATA)
{
// Compare ANSI and Unicode strings returned
(void)(WideCharToMultiByte(CP_ACP, 0, wszBuf, wcslen(wszBuf), aszConvBuf,
sizeof(aszConvBuf), NULL, NULL));
aszConvBuf[wcslen(wszBuf)]=TEXT('\0');

if (strcmp(aszBuf, aszConvBuf))
{
wsprintf(buf, TEXT("Unicode string did not match ANSI string.\r\n")\
TEXT("\t\t\tExpected: '%hs', Received: '%hs'"), aszBuf, aszConvBuf);
DISPLAYERROR(szAPI, buf);
bCompare=FALSE;
}
}

return bCompare;
}

//------------------------------------------------------------------------------
//Function:CompareAnsiUnicodeResults
//Compares an ANSI result set to a Unicode result set
//------------------------------------------------------------------------------
void CompareAnsiUnicodeResults(SQLHSTMT hstmtA, SQLHSTMT hstmtW, SQLTCHAR * szAPI)
{
SQLCHAR aszBuf[MAX_STRING_SIZE];
SQLWCHAR wszBuf[MAX_STRING_SIZE];
SQLINTEGER cbANSI, cbUnicode;
SQLUSMALLINTuwCol;
SQLSMALLINTswColCountA, swColCountW;
SQLRETURN rc, rcA, rcW;

// Check width of result sets
rc=SQLNumResultCols(hstmtA, &swColCountA);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtA, SQL_SUCCESS, rc, szSQLNUMRESULTCOLS);
rc=SQLNumResultCols(hstmtW, &swColCountW);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtW, SQL_SUCCESS, rc, szSQLNUMRESULTCOLS);
if (swColCountA != swColCountW)
DISPLAYERROR(szAPI,TEXT("ANSI and Unicode result sets different widths."));

uwCol=1;
do {
rc=SQLBindCol(hstmtA, uwCol, SQL_C_CHAR, aszBuf, sizeof(aszBuf), &cbANSI);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtA, SQL_SUCCESS, rc, szSQLBINDCOL);
rc=SQLBindCol(hstmtW, uwCol, SQL_C_WCHAR, wszBuf, sizeof(wszBuf), &cbUnicode);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtW, SQL_SUCCESS, rc, szSQLBINDCOL);

// Copy something into the buffers so we can tell if they were modified
strcpy(aszBuf, "Untouched");
wcscpy(wszBuf, L"Untouched");

// Fetch into the buffers
rcA=SQLFetch(hstmtA);
rcW=SQLFetch(hstmtW);


if (RC_SUCCESSFUL(rcA) && RC_SUCCESSFUL(rcW))
{
if (!CompareAnsiToUnicode(aszBuf, cbANSI, wszBuf, cbUnicode, szSQLFETCH))
{
rcW=SQL_NO_DATA;// Abort the loop w/o generating failure
rcA=SQL_NO_DATA;
}
}

// Unbind the column bound above on each statement
rc=SQLFreeStmt(hstmtA, SQL_UNBIND);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtA, SQL_SUCCESS, rc, szSQLFREESTMT);
rc=SQLFreeStmt(hstmtW, SQL_UNBIND);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtW, SQL_SUCCESS, rc, szSQLFREESTMT);

// Don't exceed the total number of columns in result set
uwCol++;
if (uwCol > (SQLUSMALLINT)swColCountA)
uwCol=1;
}
while (RC_SUCCESSFUL(rcA) && RC_SUCCESSFUL(rcW));

// Make sure the result sets were the same size
if (rcW != SQL_NO_DATA)
ERRSHANDLE(SQL_HANDLE_STMT, hstmtW, SQL_NO_DATA, rc, szSQLFETCH);

// Make sure the result sets were the same size
if (rcA != rcW)
DISPLAYERROR(szAPI,TEXT("ANSI and Unicode result sets not the same length."));

rc=SQLFreeStmt(hstmtA, SQL_CLOSE);

ERRSHANDLE(SQL_HANDLE_STMT, hstmtA, SQL_SUCCESS, rc, szSQLFREESTMT); 
rc=SQLFreeStmt(hstmtW, SQL_CLOSE);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtW, SQL_SUCCESS, rc, szSQLFREESTMT);

}


/*------------------------------------------------------------------------------
/Function:TestMixedAnsiUnicode
/Purpose:Call several of the Unicode functions mixed with ANSI functions
/to verify functions succeed in the mixed case.
/*----------------------------------------------------------------------------*/
void PASCAL TestMixedAnsiUnicode(QTSTRUCT * lpqt)
{
HDBChdbcANSI, hdbcUnicode;
HSTMT hstmtANSIA, hstmtANSIW, hstmtUA, hstmtUW, hstmtW;
SQLINTEGER cbANSI, cbUnicode;
SQLWCHAR wszServer[MAX_STRING_SIZE], wszLogin[MAX_STRING_SIZE],
wszPassword[MAX_STRING_SIZE], wszQuery[MAX_STRING_SIZE],
wszBuf[MAX_STRING_SIZE], wszTable[MAX_STRING_SIZE];
SQLCHARaszServer[MAX_STRING_SIZE], aszLogin[MAX_STRING_SIZE],
aszPassword[MAX_STRING_SIZE], aszQuery[MAX_STRING_SIZE],
aszBuf[MAX_STRING_SIZE], aszTable[MAX_STRING_SIZE];
SQLUSMALLINT fStmts;
SQLRETURN rc, rcA=SQL_SUCCESS, rcW=SQL_SUCCESS;

// We've already set the environment attribute to 3.0 by this point

#ifdef UNICODE
// Copy the unicode strings
lstrcpy(wszServer, lpSI->szValidServer0);
lstrcpy(wszLogin, lpSI->szValidLogin0);
lstrcpy(wszPassword, lpSI->szValidPassword0);
lstrcpy(wszTable, lpqt->szTableName);
wsprintf(wszQuery,szSELECTSTAR,lpqt->szTableName);

// Convert and copy to ANSI strings
/*
int WideCharToMultiByte(
UINT CodePage,// code page
DWORD dwFlags,// performance and mapping flags
LPCWSTR lpWideCharStr,// address of wide-character string
int cchWideChar,// number of characters in string
LPSTR lpMultiByteStr,// address of buffer for new string
int cchMultiByte,// size of buffer
LPCSTR lpDefaultChar,// address of default for unmappable characters
LPBOOL lpUsedDefaultChar // address of flag set when default char. used
);
*/
// Server
(void)(WideCharToMultiByte(CP_ACP,
0,
lpSI->szValidServer0,
lstrlen(lpSI->szValidServer0),
aszServer,
sizeof(aszServer),
NULL,
NULL));
aszServer[lstrlen(lpSI->szValidServer0)]=TEXT('\0');

// UID
(void)(WideCharToMultiByte(CP_ACP,
0,
lpSI->szValidLogin0,
lstrlen(lpSI->szValidLogin0),
aszLogin,
sizeof(aszLogin),
NULL,
NULL));
aszLogin[lstrlen(lpSI->szValidLogin0)]=TEXT('\0');

// PWD
(void)(WideCharToMultiByte(CP_ACP,
0,
lpSI->szValidPassword0,
lstrlen(lpSI->szValidPassword0),
aszPassword,
sizeof(aszPassword),
NULL,
NULL));
aszPassword[lstrlen(lpSI->szValidPassword0)]=TEXT('\0');

// Select stmt
(void)(WideCharToMultiByte(CP_ACP,
0,
wszQuery,
lstrlen(wszQuery),
aszQuery,
sizeof(aszQuery),
NULL,
NULL));
aszQuery[lstrlen(wszQuery)]=TEXT('\0');

// Table name
(void)(WideCharToMultiByte(CP_ACP,
0,
wszTable,
lstrlen(wszTable),
aszQuery,
sizeof(aszTable),
NULL,
NULL));
aszTable[lstrlen(wszTable)]=TEXT('\0');

#else
// Copy the ANSI strings
lstrcpy(aszServer, lpSI->szValidServer0);
lstrcpy(aszLogin, lpSI->szValidLogin0);
lstrcpy(aszPassword, lpSI->szValidPassword0);
lstrcpy(aszTable, lpqt->szTableName);
wsprintf(aszQuery,szSELECTSTAR,lpqt->szTableName);

// Convert and copy to Unicode strings
/*
int MultiByteToWideChar(
UINT CodePage,// code page
DWORD dwFlags,// character-type options
LPCSTR lpMultiByteStr,// address of string to map
int cchMultiByte,// number of characters in string
LPWSTR lpWideCharStr,// address of wide-character buffer
int cchWideChar // size of buffer
);
*/

// Server
(void) MultiByteToWideChar(CP_ACP,
0,
lpSI->szValidServer0,
lstrlen(lpSI->szValidServer0),
wszServer,
sizeof(wszServer)/sizeof(WCHAR)
);
wszServer[lstrlen(lpSI->szValidServer0)]=TEXT('\0');

// UID
(void) MultiByteToWideChar(CP_ACP,
0,
lpSI->szValidLogin0,
lstrlen(lpSI->szValidLogin0),
wszLogin,
sizeof(wszLogin)/sizeof(WCHAR)
);
wszLogin[lstrlen(lpSI->szValidLogin0)]=TEXT('\0');

// PWD
(void) MultiByteToWideChar(CP_ACP,
0,
lpSI->szValidPassword0,
lstrlen(lpSI->szValidPassword0),
wszPassword,
sizeof(wszPassword)/sizeof(WCHAR)
);
wszPassword[lstrlen(lpSI->szValidPassword0)]=TEXT('\0');

// Select stmt
(void) MultiByteToWideChar(CP_ACP,
0,
aszQuery,
lstrlen(aszQuery),
wszQuery,
sizeof(wszQuery)/sizeof(WCHAR)
);
wszQuery[lstrlen(aszQuery)]=TEXT('\0');

// Table name
(void) MultiByteToWideChar(CP_ACP,
0,
aszTable,
lstrlen(aszTable),
wszTable,
sizeof(wszTable)/sizeof(WCHAR)
);
wszTable[lstrlen(aszTable)]=TEXT('\0');

#endif

// Allocate connection for ANSI
rc = SQLAllocConnect(henv, &hdbcANSI);
RETCHECK(SQL_SUCCESS, rc, szSQLALLOCCONNECT);

// Allocate connection for Unicode
rc = SQLAllocConnect(henv, &hdbcUnicode);
RETCHECK(SQL_SUCCESS, rc, szSQLALLOCCONNECT);

// If Unicode is defined, then strings need to be converted to ANSI
// before calling SQLConnectA
rc = SQLConnectA(hdbcANSI, (LPSTR)aszServer, SQL_NTS,
(LPSTR)aszLogin, SQL_NTS,
(LPSTR)aszPassword, SQL_NTS);
if (!RC_SUCCESSFUL(rc))
ERRSHANDLE(SQL_HANDLE_DBC, hdbcANSI, SQL_SUCCESS, rc,szSQLCONNECT);

rc = SQLConnectW(hdbcUnicode, (LPWSTR)wszServer, SQL_NTS,
(LPWSTR)wszLogin, SQL_NTS,
(LPWSTR)wszPassword, SQL_NTS);
if (!RC_SUCCESSFUL(rc))
ERRSHANDLE(SQL_HANDLE_DBC, hdbcUnicode, SQL_SUCCESS, rc,szSQLCONNECTW);

// Test mixed SQLNativeSQL callson the ANSI connection
rc=SQLNativeSqlA(hdbcANSI, aszQuery, strlen(aszQuery), aszBuf, sizeof(aszBuf), &cbANSI);
ERRSHANDLE(SQL_HANDLE_DBC, hdbcANSI, SQL_SUCCESS, rc,szSQLNATIVESQL);
rc=SQLNativeSqlW(hdbcANSI,wszQuery, wcslen(wszQuery)*sizeof(WCHAR), wszBuf, sizeof(wszBuf),
&cbUnicode);
ERRSHANDLE(SQL_HANDLE_DBC, hdbcANSI, SQL_SUCCESS, rc,szSQLNATIVESQL);

CompareAnsiToUnicode(aszBuf, cbANSI, wszBuf, cbUnicode, szSQLNATIVESQLW);

// Allocate stmt handles for ANSI connection
rc = SQLAllocStmt(hdbcANSI, &hstmtANSIA);// Handle for ANSI calls
ERRSHANDLE(SQL_HANDLE_DBC, hdbcANSI, SQL_SUCCESS, rc,szSQLALLOCSTMT);
rc = SQLAllocStmt(hdbcANSI, &hstmtANSIW);// Handle for Unicode calls
ERRSHANDLE(SQL_HANDLE_DBC, hdbcANSI, SQL_SUCCESS, rc,szSQLALLOCSTMT);

// Allocate stmt handles for Unicode connection
rc = SQLAllocStmt(hdbcUnicode, &hstmtUA); // Handle for ANSI calls
ERRSHANDLE(SQL_HANDLE_DBC, hdbcUnicode, SQL_SUCCESS, rc,szSQLALLOCSTMT);
rc = SQLAllocStmt(hdbcUnicode, &hstmtUW); // Handle for Unicode calls
ERRSHANDLE(SQL_HANDLE_DBC, hdbcUnicode, SQL_SUCCESS, rc,szSQLALLOCSTMT);

// Call SQLTablesA&W on two different hdbcs
strcpy(aszBuf,"%");
wcscpy(wszBuf,L"%");
rc = SQLTablesA(hstmtANSIA, NULL, 0, NULL, 0, aszBuf, SQL_NTS, NULL, 0);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtANSIA, SQL_SUCCESS, rc, szSQLTABLESA);
rc = SQLTablesW(hstmtUW, NULL, 0, NULL, 0, wszBuf, SQL_NTS, NULL, 0);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtUW, SQL_SUCCESS, rc, szSQLTABLESW);

// Compare the two result sets
CompareAnsiUnicodeResults(hstmtANSIA, hstmtUW, szSQLTABLESW);

// See if the driver supports multiple active stmts
rc = SQLGetInfo(hdbcANSI, SQL_ACTIVE_STATEMENTS, &fStmts, 0, NULL);
ERRSHANDLE(SQL_HANDLE_DBC, hdbcANSI, SQL_SUCCESS, rc, szSQLGETINFO);

// Create a search string for ANSI
aszBuf[0]=aszTable[0];
aszBuf[1]=TEXT('\0');
strcat(aszBuf,"%");

// Create a search string for Unicode
wszBuf[0]=wszTable[0];
wszBuf[1]=TEXT('\0');
wcscat(wszBuf,L"%");

// If the allowed active statements is > 1, then use the same connection
// otherwise use the other connection
if (fStmts > 1)
hstmtW=hstmtUW;
else
hstmtW=hstmtANSIW;

// Call SQLColumnsA&W on two different hstmts (same connection if allowed)
// Call for only the given table to limit compare errors at this time
rc = SQLColumnsA(hstmtUA, NULL, 0, NULL, 0, aszTable, SQL_NTS, NULL, 0);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtANSIA, SQL_SUCCESS, rc, szSQLCOLUMNSA);

// Call for only the given table to limit compare errors at this time
rc = SQLColumnsW(hstmtW, NULL, 0, NULL, 0, wszTable, SQL_NTS, NULL, 0);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtW, SQL_SUCCESS, rc, szSQLCOLUMNSW);

CompareAnsiUnicodeResults(hstmtUA, hstmtW, szSQLCOLUMNSW);

rc = SQLExecDirectA(hstmtUA, aszQuery, SQL_NTS);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtUA, SQL_SUCCESS, rc, szSQLEXECDIRECTA);

// Call for only the given table to limit compare errors at this time
rc = SQLExecDirectW(hstmtW, wszQuery, SQL_NTS);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtW, SQL_SUCCESS, rc, szSQLEXECDIRECTW);

CompareAnsiUnicodeResults(hstmtUA, hstmtW, szSQLEXECDIRECTW);

// Disconnect and free ANSI handles
rc=SQLFreeStmt(hstmtANSIA, SQL_DROP);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtANSIA, SQL_SUCCESS, rc,szSQLFREESTMT);
rc=SQLFreeStmt(hstmtANSIW, SQL_DROP);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtANSIW, SQL_SUCCESS, rc,szSQLFREESTMT);
rc=SQLDisconnect(hdbcANSI);
ERRSHANDLE(SQL_HANDLE_DBC, hdbcANSI, SQL_SUCCESS, rc,szSQLCONNECT);
rc=SQLFreeConnect(hdbcANSI);
ERRSHANDLE(SQL_HANDLE_DBC, hdbcANSI, SQL_SUCCESS, rc,szSQLCONNECT);
// Disconnect and free Unicode handles
rc=SQLFreeStmt(hstmtUA, SQL_DROP);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtUA, SQL_SUCCESS, rc,szSQLFREESTMT);
rc=SQLFreeStmt(hstmtUW, SQL_DROP);
ERRSHANDLE(SQL_HANDLE_STMT, hstmtUW, SQL_SUCCESS, rc,szSQLFREESTMT);
rc=SQLDisconnect(hdbcUnicode);
ERRSHANDLE(SQL_HANDLE_DBC, hdbcUnicode, SQL_SUCCESS, rc,szSQLCONNECT);
rc=SQLFreeConnect(hdbcUnicode);
ERRSHANDLE(SQL_HANDLE_DBC, hdbcUnicode, SQL_SUCCESS, rc,szSQLCONNECT);

} /* TestMixedAnsiUnicode() */

//-----------------------------------------------------------------------
// Function: TestStmtOptions
//-----------------------------------------------------------------------

void PASCAL TestStmtOptions()
{
UDWORDdwLen,
udw;
BOOLfConnectOption;
RETCODErc=SQL_SUCCESS;
TCHARszAPI[MEDBUFF];
SDWORDcbValue=0;

SetStatementAttributes(SQL_MAX_LENGTH,(PTR)&udw);

if (SetStatementAttributes(SQL_MAX_LENGTH, (PTR)TEST_STMT_OPTION))
fConnectOption = TRUE;

if (Supported(SQL_API_SQLGETSTMTATTR))
{
rc = SQLGetStmtAttr(hstmt, SQL_MAX_LENGTH, (PTR)&dwLen,sizeof(dwLen),&cbValue);
lstrcpy(szAPI,szSQLGETSTMTATTR);
}
else
{
rc = SQLGetStmtOption(hstmt, SQL_MAX_LENGTH, &dwLen);
lstrcpy(szAPI,szSQLGETSTMTOPTION);
}

if(rc != SQL_SUCCESS)
{
if(!FindError(SQL_HANDLE_STMT,szHYC00))
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTOPTION);
}
else
{
if(dwLen != TEST_STMT_OPTION && rc == SQL_SUCCESS && fConnectOption)
DISPLAYERROR(szSQLGETSTMTOPTION,TEXT("incorrect SQL_MAX_LENGTH value returned"));
}


} //TestStmtOptions()


//-----------------------------------------------------------------------
// Function: SetTableName
//-----------------------------------------------------------------------

void PASCAL SetTableName(QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
UWORDcTableName;
TCHARszTime[MEDBUFF]=TEXT(""),
szTmpBuff[MEDBUFF],
*pTime=NULL;

_tstrtime(szTmpBuff);

/* Remove colons */
pTime=_tcstok(szTmpBuff, TEXT(":"));
while (pTime)
{
lstrcat(szTime,pTime);
pTime=_tcstok(NULL, TEXT(":"));
}

rc = SQLGetInfo(hdbc, SQL_MAX_TABLE_NAME_LEN, &cTableName,
sizeof (int), NULL);
if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

rc = SQLGetInfo(hdbc, SQL_QUALIFIER_NAME_SEPARATOR, lpqt->buf,
MAX_STRING_SIZE, NULL);
if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

if(!lstrcmp(TEXT("\\"), lpqt->buf))
cTableName -= 4;

_sntprintf(lpqt->szTableName, min(cTableName, MAX_TABLE_NAME-1),
TEXT("q%s"), szTime);

} //SetTableName()


//-----------------------------------------------------------------------
// Function: AllowedType
//
//If swNumTypes > 0, limits SQL types to those in array
//-----------------------------------------------------------------------
BOOL AllowedType(SWORD fType)
{
SWORD i, swNumTypes=0;
BOOL bAllowed=TRUE;
SWORD rgAllowedTypes[MAX_TYPES_SUPPORTED]={
SQL_CHAR,
SQL_BINARY
//SQL_BIT
/*
SQL_TINYINT
SQL_NUMERIC,
SQL_INTEGER,
SQL_SMALLINT,
SQL_FLOAT,
SQL_REAL,
SQL_TYPE_TIMESTAMP,
SQL_LONGVARBINARY,
SQL_LONGVARCHAR
*/
};

if (swNumTypes > 0)
{
bAllowed=FALSE;

for (i=0; i < swNumTypes; i++)
{
if (fType == rgAllowedTypes[i])
bAllowed=TRUE;
}
}

return bAllowed;
}

//-----------------------------------------------------------------------
// Function: GetTypeInfo
//-----------------------------------------------------------------------

UWORD PASCAL GetTypeInfo(FIELDINFO *rgFields,QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
UWORD cFieldName;
SDWORD cMaxTypeSize, cMaxRowSize;
UWORD i,
cTypes=0;
SDWORD sdw;
LPTSTR pch=NULL;
BOOLfAutoInc=0;

/* Get the type information to use in a create statement for a table */

rc = SQLGetInfo(hdbc, SQL_MAX_COLUMN_NAME_LEN, &cFieldName,
sizeof cFieldName, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

rc = SQLGetInfo(hdbc, SQL_MAX_ROW_SIZE, &cMaxRowSize,
sizeof cMaxRowSize, NULL);
/* don't check for SQL_SUCCES here, it's a 2.0 function. If it's successful
That's great, if not then no maximum is assumed. */

cMaxTypeSize = cMaxRowSize / MAX_TYPES_SUPPORTED;

/* TO DO: add checks in here for dos file types GetInfo SQL_QUALIFIER_NAME_SEPARATER */

if(cFieldName > MAX_FIELD - 1)
cFieldName = MAX_FIELD - 1;

if(cFieldName < PREFIX_SIZE)
{
DISPLAYERROR(szSQLGETINFO,TEXT("MAX_COLUMN_NAME_LEN is too small for autotest to run"));
goto TypeErrorRet;
}

rc = SQLGetTypeInfo(hstmt, SQL_ALL_TYPES);
RETCHECK(SQL_SUCCESS, rc,szSQLGETTYPEINFO);

for(i = 0; i < MAX_TYPES_SUPPORTED; i++)
{

rc = SQLFetch(hstmt);
if(rc != SQL_SUCCESS && rc !=SQL_SUCCESS_WITH_INFO)
break;

*rgFields[i].szType = *rgFields[i].szLength = *rgFields[i].szParams = TEXT('\0');

/* get type name */
rc = SQLGetData(hstmt, 1, SQL_C_TCHAR,
rgFields[i].szType, MAX_TYPE_NAME, &sdw);

if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

wsprintf(rgFields[i].szFieldName,TEXT("c%02u"), i+COLNAME_START);
/* copy first portion of type name for easy reference */
_tcsnccat(rgFields[i].szFieldName, rgFields[i].szType, TYPE_PORTION);


/* change spaces in field name to '_' */
while((pch = (LPTSTR)_tcschr(rgFields[i].szFieldName, TEXT(' '))) != NULL)
*pch = TEXT('_');

/* get the SQLType */
rc = SQLGetData(hstmt, 2, SQL_C_DEFAULT,
(SWORD *)&rgFields[i].wSQLType, IGNORED,
&sdw);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

/* Max length */
rc = SQLGetData(hstmt, 3, SQL_C_TCHAR,
rgFields[i].szLength, MAX_FIELD, &sdw);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

/* limit the row size for those drivers that don't support a long
enough row length for all the fields to be at the max length */
switch(rgFields[i].wSQLType)
{
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_VARBINARY:
case SQL_BINARY:
case SQL_LONGVARCHAR:
case SQL_LONGVARBINARY:

if(cMaxTypeSize)
{
if(_ttol (rgFields[i].szLength) > cMaxTypeSize)
_ltot(cMaxTypeSize, rgFields[i].szLength, 10);
}
}

/* prefix */
rc = SQLGetData(hstmt, 4, SQL_C_TCHAR,
rgFields[i].szPrefix, MAX_PREFIX, &sdw);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

/* suffix */
rc = SQLGetData(hstmt, 5, SQL_C_TCHAR,
rgFields[i].szSuffix, MAX_SUFFIX, &sdw);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

/* create params */
rc = SQLGetData(hstmt, 6, SQL_C_TCHAR,
rgFields[i].szParams, MAX_PARAM_SIZE, &sdw);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

/* nullable */
rc = SQLGetData(hstmt, 7, SQL_C_SHORT, &rgFields[i].nullable, IGNORED,
&sdw);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

/* searchable */
rc = SQLGetData(hstmt, 9, SQL_C_SHORT, &rgFields[i].fSearchable, IGNORED,
&sdw);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

/* autoincrement */
rc = SQLGetData(hstmt, 12, SQL_C_SHORT, &rgFields[i].autoinc, IGNORED,
&sdw);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA))
goto TypeErrorRet;

// Since some data types will have NULL for autoincrement, we need to set to FALSE
if (SQL_NULL_DATA == sdw)
rgFields[i].autoinc=FALSE;

if (IgnoreType(rgFields[i].szType,rgFields[i].wSQLType))
{
i--;
continue;
}

/* The following code only allows the first autoincrement column to
be placed in the table. Many DBMS's do not allow more than one per table. */
if(rgFields[i].autoinc == TRUE)
{
if(fAutoInc)
{
i--;
continue;
}
else
fAutoInc = TRUE;
}

// Limit data types to those desired (use for debugging)
if (!AllowedType(rgFields[i].wSQLType))
{
i--;
continue;
}
}

cTypes = i;

/* if the type name contains spaces, replace them with _ because
most servers don't allow spaces in field names */

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

rc = SQLFreeStmt(hstmt, SQL_CLOSE);
RETCHECK(SQL_SUCCESS, rc,szSQLFREESTMT);

SetTableName(lpqt);

return(cTypes);

TypeErrorRet:

return(0);

} //GetTypeInfo()




//-----------------------------------------------------------------------
// Function: BuildCreateStmt
//-----------------------------------------------------------------------

BOOL PASCAL BuildCreateStmt(QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes)
{
RETCODErc=SQL_SUCCESS;
TCHAR szNum[MAX_NUM_BUFFER]; /* used with _ttoi and itoa conversions */
SWORD wNum;
UWORD i;
SDWORD sdw;
LPTSTR pch=NULL;


/* the column names will be ctypname (where typename is the
type name returned by the source in SQLGetTypeInfo) */

*lpqt->sz = TEXT('\0');
lstrcpy(lpqt->sz,TEXT("Create Table "));
lstrcat(lpqt->sz, lpqt->szTableName);
lstrcat(lpqt->sz,TEXT(" ("));
for(i = 0; i < cTypes; i++) {
TCHAR szParamType[50];
TCHAR szNotNull[10];
*lpqt->szParam = TEXT('\0');

/* if SQLGetTypeInfo returned params they need to be used in
the create statement */

if(rgFields[i].szParams == NULL || lstrlen(rgFields[i].szParams) == 0)
*lpqt->szParam = TEXT('\0');
else if(_tcschr(rgFields[i].szParams, TEXT(',')) == NULL)
wsprintf(lpqt->szParam,TEXT("(%s)"), rgFields[i].szLength);
else
{
lstrcpy(szNum, rgFields[i].szLength);
wsprintf(lpqt->szParam,TEXT("(%s, %d)"), rgFields[i].szLength,
_ttoi(szNum) - SCALEDIFF);
}

lstrcpy(szParamType, rgFields[i].szType);
if(pch = _tcschr((LPTSTR)szParamType, TEXT('(')))
{
*pch = TEXT('\0');
lstrcat(szParamType, lpqt->szParam);
lstrcat(szParamType, (LPTSTR)_tcschr(rgFields[i].szType, TEXT(')')) + 1);
}
else
{
lstrcat(szParamType, lpqt->szParam);
}

/* If the field is not nullable, we need to create the table that way */
rc = SQLGetInfo(hdbc, SQL_NON_NULLABLE_COLUMNS, &wNum,
sizeof (SWORD), NULL);
if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

if((rgFields[i].nullable == SQL_NO_NULLS) && (wNum != SQL_NNC_NULL))
lstrcpy(szNotNull,TEXT("NOT NULL"));
else
szNotNull[0] = TEXT('\0');

wsprintf(&lpqt->sz[lstrlen(lpqt->sz)],TEXT(" %s %s %s ,"),
rgFields[i].szFieldName, (LPTSTR)szParamType, (LPTSTR)szNotNull);
}

/* remove the last comma and space */
lpqt->sz[lstrlen(lpqt->sz) - 2] = TEXT('\0');
lstrcat(lpqt->sz,TEXT(")"));

rc = SQLExecDirect(hstmt, lpqt->sz, SQL_NTS);
if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
if(!RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT))
goto BuildErrorRet;

/* now that table was created, call SQLColumns. Both as
a test and to get necessary information to insert data */

rc = SQLColumns(hstmt, NULL, SQL_NTS, NULL, SQL_NTS,
lpqt->szTableName, SQL_NTS, NULL, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLCOLUMNS);

for(i = 0; i < cTypes; i++) {
rc = SQLFetch(hstmt);
RETCHECK(SQL_SUCCESS, rc,szSQLFETCH);

/* precision */
rc = SQLGetData(hstmt, 7, SQL_C_LONG, &rgFields[i].precision, IGNORED,
&sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);

/* length */
if((rgFields[i].precision == 0) || (sdw == SQL_NO_TOTAL)){
rc = SQLGetData(hstmt, 8, SQL_C_LONG, &rgFields[i].precision, IGNORED,
&sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);
if(sdw == SQL_NO_TOTAL)/* precision & length were undetermined */
rgFields[i].precision = 1000;/* so set it to an arbitary value */
}

/* numeric scale */
rc = SQLGetData(hstmt, 9, SQL_C_SHORT, &rgFields[i].scale, IGNORED,
&sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);
if(sdw == SQL_NO_TOTAL)
rgFields[i].scale = 0;
}

rc = SQLFreeStmt(hstmt, SQL_CLOSE);
RETCHECK(SQL_SUCCESS, rc,szSQLFREESTMT);

/* look for columns the test should not try to update by running
a select * query and calling ColAttributes on the result fields */
/* select * does not return the fields in a defined order, so field order
must be specified */

lstrcpy(lpqt->sz,TEXT("select "));
for(i = 0; i < cTypes; i ++) {
if(i)
lstrcat(lpqt->sz,TEXT(","));
lstrcat(lpqt->sz, rgFields[i].szFieldName);
}

lstrcat(lpqt->sz,TEXT(" from "));
lstrcat(lpqt->sz, lpqt->szTableName);

rc = SQLExecDirect(hstmt, lpqt->sz, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

for(i = 1; i <= cTypes; i++) {

rc = SQLColAttributes(hstmt, i, SQL_COLUMN_UPDATABLE, NULL,
0, NULL, &sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLCOLATTRIBUTES);

rgFields[i - 1].fAutoUpdate = sdw == SQL_ATTR_READONLY;

rc = SQLColAttributes(hstmt, i, SQL_COLUMN_UNSIGNED, NULL,
0, NULL, &sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLCOLATTRIBUTES);

rgFields[i - 1].fUnsigned = sdw;
}

rc = SQLFreeStmt(hstmt, SQL_CLOSE);
RETCHECK(SQL_SUCCESS, rc,szSQLFREESTMT);

return(FALSE);

BuildErrorRet:

return(TRUE);

} //BuildCreateStmt()



//-----------------------------------------------------------------------
// Function: BuildInsertStmt
//-----------------------------------------------------------------------

BOOL PASCAL BuildInsertStmt(QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes,
DSTRUCT *lpd,SDWORD *cColsSelected,BOOL fBindParameter)
{
RETCODE rc;
UWORD w;
UWORD i;
PTRptr;
UWORD ind;
SDWORD sdw;
LPTSTR pch=NULL;
BOOLfColNames=TRUE,
fLongData=FALSE;
SDWORD *sdwArray = (SDWORD *)AllocateMemory(sizeof(SDWORD) * MAX_TYPES_SUPPORTED);

lstrcpy(lpqt->szColNames,TEXT("\0"));
lstrcpy(lpqt->szValues,TEXT("\0"));

for(i = 0; i < cTypes; i++) {

for(ind = 0, w = 0; ind < cTypes; ind++) {

pch = qtMakeData(i, ind,&rgFields[ind], lpqt->szDataItem);

if(!pch) /* current type is READ_ONLY) */
continue;

/* for every nullable type, that field will be set to */
/* null when the row number corresponds with the field */
/* number */

if(*pch)
{
lstrcpy(lpd[w].data, pch);
lpd[w].cb = SQL_NTS;
}
else
{
lstrcpy(lpd[w].data,TEXT(""));
lpd[w].cb = SQL_NULL_DATA;
}

if(fColNames)
{
/* the first time throught, the insert */
/* statement itself is created */
lstrcat(lpqt->szColNames, rgFields[ind].szFieldName);
lstrcat(lpqt->szColNames,TEXT(", "));
lstrcat(lpqt->szValues,TEXT(" ?, "));

/* and parameters are set */
/* set all the parameters using pointers to buffers in*/
/* the param struct. */
if(!fBindParameter)
{
rc = SQLSetParam(hstmt, (UWORD)(w+1),
SQL_C_TCHAR, rgFields[ind].wSQLType,
rgFields[ind].precision, rgFields[ind].scale,
&lpd[w].data,
&lpd[w].cb);
RETCHECK(SQL_SUCCESS, rc,szSQLSETPARAM);
}
else
{
rc = SQLBindParameter(hstmt, (UWORD)(w+1),
SQL_PARAM_INPUT,
SQL_C_TCHAR, rgFields[ind].wSQLType,
rgFields[ind].precision, rgFields[ind].scale,
&lpd[w].data, MAX_STRING_SIZE,
&lpd[w].cb);
RETCHECK(SQL_SUCCESS, rc,szSQLBINDPARAMETER);
}
}
w++;
cNumResSetCols=w;
}

if(fColNames)
{
/* the first time through, the insert*/
/* statement needs to be SQLPrepare'd*/
/* remove the "," at the end of the string*/
lpqt->szColNames[lstrlen(lpqt->szColNames) - 2] = TEXT('\0');
lpqt->szValues[lstrlen(lpqt->szValues) - 2] = TEXT('\0');

wsprintf(lpqt->sz,TEXT("insert into %s (%s) VALUES(%s)"),
lpqt->szTableName, lpqt->szColNames, lpqt->szValues);

//copy the insert statement, with the column names.
//_ASSERTE(lstrlen(lpqt->sz) < MAX_STRING_SIZE);
lstrcpy(szInsertStmt,lpqt->sz);

//prepare the statement
rc = SQLPrepare(hstmt, lpqt->sz, SQL_NTS);
if(!RETCHECK(SQL_SUCCESS, rc,szSQLPREPARE))
goto InsertErrorRet;

fColNames = FALSE; /* no more first times through */
}

rc = SQLExecute(hstmt); /* insert a row */

if(!RETCHECK(SQL_SUCCESS, rc,szSQLEXECUTE))
{
if (SQL_SUCCESS_WITH_INFO != rc)// BANDAID, normally would fail on S.W.I
goto InsertErrorRet;
}

rc = SQLRowCount(hstmt, &sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLROWCOUNT);

if(sdw != 1) /* the execute inserted 1 row */
DISPLAYERROR(szSQLROWCOUNT,TEXT("Insert single row"));

/* FreeStmt SQL_CLOSE */
rc = SQLFreeStmt(hstmt, SQL_CLOSE);
RETCHECK(SQL_SUCCESS, rc,szSQLFREESTMT);
}

/* for the last row use SQLParamData and SQLPutData */

/* First check to see if the SQL_LEN_DATA_AT_EXEC macro is supported */
rc = SQLGetInfo(hdbc, SQL_NEED_LONG_DATA_LEN, lpqt->buf, (SWORD)sizeof(lpqt->buf), NULL);
if(rc == SQL_SUCCESS)
if(lpqt->buf[0] == TEXT('Y'))
fLongData = TRUE;

for(ind = 0, w = 0; ind < cTypes; ind++)
{

/* set all the params */

if(rgFields[ind].fAutoUpdate)
continue;

if(fLongData && ((rgFields[ind].wSQLType == SQL_LONGVARBINARY) ||
(rgFields[ind].wSQLType == SQL_LONGVARCHAR))){
/* Make sure we don't exceed the max negative SDWORD value */
if(rgFields[ind].precision > (MAXSDWORD + SQL_LEN_DATA_AT_EXEC_OFFSET))
sdwArray[ind] = MINSDWORD - SQL_LEN_DATA_AT_EXEC_OFFSET;
else
sdwArray[ind] = SQL_LEN_DATA_AT_EXEC(rgFields[ind].precision);
}
else
sdwArray[ind] = SQL_DATA_AT_EXEC;

w++;
if(!fBindParameter){
rc = SQLSetParam(hstmt, w,
SQL_C_TCHAR, rgFields[ind].wSQLType,
rgFields[ind].precision, rgFields[ind].scale,
(LPTSTR)(UDWORD)ind, &sdwArray[ind]);
RETCHECK(SQL_SUCCESS, rc,szSQLSETPARAM);
}
else{
rc = SQLBindParameter(hstmt, w, SQL_PARAM_INPUT,
SQL_C_TCHAR, rgFields[ind].wSQLType,
rgFields[ind].precision, rgFields[ind].scale,
(LPTSTR)(((UDWORD)ind)+BIND_PARM_OFFSET), 0, &sdwArray[ind]);
RETCHECK(SQL_SUCCESS, rc,szSQLBINDPARAMETER);
}
}

*cColsSelected = w;

rc = SQLExecDirect(hstmt, lpqt->sz, SQL_NTS);
if (!RETCHECK(SQL_NEED_DATA, rc,szSQLEXECDIRECT))
goto InsertErrorRet;


for(ind = 0; ind <= cTypes; ind++)
{
rc = SQLParamData(hstmt, &ptr);
if(rc != SQL_NEED_DATA)
break;

pch = qtMakeData(cTypes, ind, &rgFields[(unsigned long)ptr-BIND_PARM_OFFSET],
lpqt->szDataItem);

if(*pch) {
rc = SQLPutData(hstmt, pch, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPUTDATA);
} else {
rc = SQLPutData(hstmt, (LPTSTR)IGNORED, SQL_NULL_DATA);
RETCHECK(SQL_SUCCESS, rc,szSQLPUTDATA);
}
}

RETCHECK(SQL_SUCCESS, rc,szSQLPARAMDATA);

rc = SQLRowCount(hstmt, &sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLROWCOUNT);

if(sdw != 1)
DISPLAYERROR(szSQLROWCOUNT,TEXT("Insert single row"));

rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS);

RETCHECK(SQL_SUCCESS, rc,szSQLFREESTMT); 



ReleaseMemory(sdwArray);

return(FALSE);

InsertErrorRet:

ReleaseMemory(sdwArray);

return(TRUE);

} //BuildInsertStmt()


//-----------------------------------------------------------------------
// Function: FullDisconnect
//-----------------------------------------------------------------------

void PASCAL FullDisconnect()
{
RETCODE rc=SQL_SUCCESS;

/* SQLCancel has the same functionality as SQLFreeStmt w/ SQL_CLOSE*/
/*in a non-asynchronous environment */

rc = SQLCancel(hstmt);
RETCHECK(SQL_SUCCESS, rc,szSQLCANCEL);

FreeStmt(SQL_DROP);

/* if the connection was made in the*/
/*test, it should be disconnected*/
/* in the test, otherwise it should be left connected */

rc = SQLDisconnect(hdbc);
RETCHECK(SQL_SUCCESS, rc,szSQLDISCONNECT);

rc = SQLFreeConnect(hdbc);
RETCHECK(SQL_SUCCESS, rc,szSQLFREECONNECT);

rc = SQLFreeEnv(henv);
RETCHECK(SQL_SUCCESS, rc,szSQLFREEENV);


} //FullDisconnect()



//-----------------------------------------------------------------------
// Function: DropTable
//-----------------------------------------------------------------------

void PASCAL DropTable(LPTSTR szTableName, BOOL fSilent)
{
TCHARszBuff[100];
RETCODErc=SQL_ERROR;

lstrcpy(szBuff,TEXT("drop table "));
lstrcat(szBuff, szTableName);

rc = SQLExecDirect(hstmt, szBuff, SQL_NTS);

if (!fSilent)
{
if(!RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT))
szWrite(TEXT("Unable to drop table"), TRUE);
}

} //DropTable()



//-----------------------------------------------------------------------
// Function:CleanUp
//Purpose:To drop the table,disconnect from data source and
//release memory.
//-----------------------------------------------------------------------

void PASCAL CleanUp(DSTRUCT *lpd,QTSTRUCT *lpqt,FIELDINFO *rgFields,
LPTSTR szTableName,BOOL fSilent,LPTSTR szValidServer)
{

DropTable(szTableName, fSilent);

if(szValidServer)
FullDisconnect();

ReleaseMemory(lpd);
ReleaseMemory(lpqt);
ReleaseMemory(rgFields);

} //CleanUp()


//-----------------------------------------------------------------------
// Function: BindAllColumns
//-----------------------------------------------------------------------

BOOL PASCAL BindAllColumns(QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes,
DSTRUCT *lpd)
{
//-----------------------------------------------------------------------
// Function: BindAllColumns
UWORDi,
w;
RETCODErc=SQL_SUCCESS;

for(i = 0, w = 0; i < cTypes; i++)
{

if(!qtMakeData(1, i,&rgFields[i],lpqt->szDataItem))
continue;

w++;

switch(rgFields[i].wSQLType)
{
case SQL_INTEGER:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpd[i].sdword, IGNORED, &lpd[i].cb);
break;
case SQL_SMALLINT:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpd[i].sword, IGNORED, &lpd[i].cb);
break;
case SQL_FLOAT:
case SQL_DOUBLE:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpd[i].sdouble, IGNORED, &lpd[i].cb);
break;
case SQL_REAL:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpd[i].sfloat, IGNORED, &lpd[i].cb);
break;
case SQL_DATE:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpd[i].date, IGNORED, &lpd[i].cb);
break;
case SQL_TIME:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpd[i].time, IGNORED, &lpd[i].cb);
break;
case SQL_TIMESTAMP:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpd[i].timestamp, IGNORED, &lpd[i].cb);
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_NUMERIC:
case SQL_DECIMAL:
default:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
lpd[i].data, MAX_STRING_SIZE, &lpd[i].cb);
break;
}

if(!RETCHECK(SQL_SUCCESS, rc, szSQLBINDCOL))
return(FALSE);
}

return(TRUE);

} //BindAllColumns()

/*-----------------------------------------------------------------------
* Function: BindFetchColumns
* Used for Checking data in Result sets from fetch calls.
* Binds Binary data types as SQL_C_CHAR
*----------------------------------------------------------------------- */

BOOL PASCAL BindFetchColumns(QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes,
DSTRUCT *lpdFetch)
{
UWORDi,
w;
RETCODErc=SQL_SUCCESS;

for(i = 0, w = 0; i < cTypes; i++)
{

if(!qtMakeData(0, i,&rgFields[i],lpqt->szDataItem))
continue;

w++;

switch(rgFields[i].wSQLType)
{
case SQL_BIGINT:
case SQL_INTEGER:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpdFetch[i].sdword, IGNORED, &lpdFetch[i].cb);
break;
case SQL_TINYINT:
case SQL_SMALLINT:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpdFetch[i].sword, IGNORED, &lpdFetch[i].cb);
break;
case SQL_FLOAT:
case SQL_DOUBLE:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpdFetch[i].sdouble, IGNORED, &lpdFetch[i].cb);
break;
case SQL_REAL:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpdFetch[i].sfloat, IGNORED, &lpdFetch[i].cb);
break;
case SQL_DATE:
case SQL_TYPE_DATE:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpdFetch[i].date, IGNORED, &lpdFetch[i].cb);
break;
case SQL_TIME:
case SQL_TYPE_TIME:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpdFetch[i].time, IGNORED, &lpdFetch[i].cb);
break;
case SQL_TIMESTAMP:
case SQL_TYPE_TIMESTAMP:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
&lpdFetch[i].timestamp, IGNORED, &lpdFetch[i].cb);
break;
case SQL_BIT:
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
rc = SQLBindCol(hstmt, w, SQL_C_TCHAR,
lpdFetch[i].data, MAX_STRING_SIZE, &lpdFetch[i].cb);
break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_NUMERIC:
case SQL_DECIMAL:
default:
rc = SQLBindCol(hstmt, w, SQL_C_DEFAULT,
lpdFetch[i].data, MAX_STRING_SIZE, &lpdFetch[i].cb);
break;
}

if(!RETCHECK(SQL_SUCCESS, rc, szSQLBINDCOL))
return(FALSE);
}

return(TRUE);

} /*BindFetchColumns() */

//-----------------------------------------------------------------------
// Function: TestMetaData
//-----------------------------------------------------------------------

void PASCAL TestMetaData(lpSERVERINFO lpSI, QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes)
{
SWORD wNum;
UWORD w;
UWORD i;
UDWORD dwLen;
RETCODErc=SQL_SUCCESS;
SWORDwType;
SWORDwNull;

SelectFromTable(lpqt);

for(i = 0, w = 0; i < cTypes; i++)
{
dwLen = 0;
wNum = 0;
wNull = 0;

/* information returned by SQLDescribeCol should match info */
/*used to create table */

if(!qtMakeData(1, i,&rgFields[i],lpqt->szDataItem))
continue;

w++;
rc = SQLDescribeCol(hstmt, w, lpqt->buf,
MAX_STRING_SIZE, NULL, &wType, &dwLen, &wNum, &wNull);
RETCHECK(SQL_SUCCESS, rc,szSQLDESCRIBECOL);

/* verify that column name returned is column name created */

if(0 != lstrcmpi(rgFields[i].szFieldName, lpqt->buf))
DISPLAYERROR(szSQLDESCRIBECOL,TEXT("incorrect column name"));

if(wType != rgFields[i].wSQLType)
{
// For a 2.x driver, SQLGetTypeInfo result will be SQL_DATE, etc,
// but 3.0 DM will convert SQLDescribeCol values to TYPE_DATE, ETC.
if ((uDriverODBCVer < 3) &&
((SQL_TYPE_DATE == wType && SQL_DATE == rgFields[i].wSQLType) ||
(SQL_TYPE_TIME == wType && SQL_TIME == rgFields[i].wSQLType) ||
(SQL_TYPE_TIMESTAMP == wType && SQL_TIMESTAMP == rgFields[i].wSQLType)))
NULL;
else
DISPLAYERROR(szSQLDESCRIBECOL,TEXT("incorrect SQLType"));
}

if(dwLen != (UDWORD)rgFields[i].precision)
DISPLAYERROR(szSQLDESCRIBECOL,TEXT("incorrect precision"));

if(wNum != rgFields[i].scale)
DISPLAYERROR(szSQLDESCRIBECOL,TEXT("incorrect scale"));

if(wNull != rgFields[i].nullable && wNull != SQL_NULLABLE_UNKNOWN &&
rgFields[i].nullable != SQL_NULLABLE_UNKNOWN)
{
DISPLAYERROR(szSQLDESCRIBECOL,TEXT("incorrect nullable"));
}
}

FreeStmt(SQL_CLOSE);

} //TestMetaData()


//-----------------------------------------------------------------------
// Function: RetrieveData
//-----------------------------------------------------------------------

void PASCAL RetrieveData(QTSTRUCT *lpqt,FIELDINFO *rgFields,
UWORD cTypes,DSTRUCT *lpd)
{
RETCODErc=SQL_SUCCESS;
UWORDind,
i;
intnSame;
LPTSTRpch=NULL;
TCHARszNum[MAX_NUM_BUFFER]; /* used with _ttoi and itoa conversions */
TCHAR*stopstring=NULL;
doubleNum=0,
Num2=0;

for(ind = 0; ;ind++)
{

/* Get the data back */
rc = SQLFetch(hstmt);
if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
break;

for(i = 0; i < cTypes; i++)
{
/* make sure it's original data placed */
/* in that field/row location */
pch = qtMakeData(ind, i,&rgFields[i], lpqt->szDataItem);

if(!pch)
continue;

if(!*pch) {
if(lpd[i].cb != SQL_NULL_DATA)
DISPLAYERROR(szRETDATA, TEXT("should have been NULL"));
continue;
}
switch(rgFields[i].wSQLType)
{
/* check the outlen and data*/
/* returned for each type. */
case SQL_INTEGER:
if(lpd[i].cb != sizeof(SDWORD))
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

lstrcpy(szNum, pch);
nSame = _ttol(szNum) == lpd[i].sdword;
break;
case SQL_SMALLINT:
if(lpd[i].cb != sizeof(SWORD))
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

lstrcpy(szNum, pch);
nSame = _ttoi(szNum) == lpd[i].sword;
break;

case SQL_FLOAT:
case SQL_DOUBLE:
if(lpd[i].cb != sizeof(SDOUBLE))
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

lstrcpy(szNum, pch);
Num=_tcstod(szNum, &stopstring);
nSame = Num - lpd[i].sdouble < 0.001 && Num - lpd[i].sdouble > -0.001;

break;

case SQL_REAL:
if(lpd[i].cb != sizeof(SFLOAT))
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

lstrcpy(szNum, pch);
Num=_tcstod(szNum, &stopstring);

nSame = Num - lpd[i].sfloat < 0.001 && Num - lpd[i].sfloat > -0.001;
break;

case SQL_TINYINT:
if(lpd[i].cb != sizeof(TCHAR))
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

lstrcpy(szNum, pch);
nSame = (TCHAR)_ttoi(szNum) == (TCHAR)lpd[i].data[0];
break;

case SQL_DECIMAL:
case SQL_NUMERIC:
if(lpd[i].cb > MAX_STRING_SIZE)
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

if(lpd[i].cb > lstrlen(lpd[i].data))
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

{
TCHAR szNum2[MAX_NUM_BUFFER];

lstrcpy(szNum, pch);
lstrcpy(szNum2, pch);
Num=_tcstod(szNum, &stopstring);
Num2=_tcstod(szNum2, &stopstring);

nSame = Num - Num2 < 0.001 &&Num - Num2 > -0.001;
}

break;


case SQL_VARCHAR:
case SQL_LONGVARCHAR:
if(lpd[i].cb > MAX_STRING_SIZE)
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

if(lpd[i].cb > lstrlen(lpd[i].data))
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

nSame = !!_tcsstr(lpd[i].data, pch);

break;

case SQL_VARBINARY:
case SQL_LONGVARBINARY:
if(lpd[i].cb > MAX_STRING_SIZE)
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

if(lpd[i].cb * 2 != lstrlen(pch))
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

nSame = TRUE; /* not checking returned data for binary */

break;

case SQL_CHAR:
if(lpd[i].cb > MAX_STRING_SIZE && rc == SQL_SUCCESS)
DISPLAYERROR(szRETDATA,TEXT("incorrect return code for outlen"));

if(lpd[i].cb != rgFields[i].precision && rc == SQL_SUCCESS)
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);

nSame = !_tcsnccmp(lpd[i].data, pch, lstrlen(pch));

break;
case SQL_BINARY:
default:
if(lpd[i].cb > MAX_STRING_SIZE && rc == SQL_SUCCESS)
DISPLAYERROR(szRETDATA,szINCORRECTOUTLEN);
nSame = TRUE; /* not checking returned data for binary */
}

if(!nSame)
DISPLAYERROR(szRETDATA, TEXT("invalid data"));
}
}

RETCHECK(SQL_NO_DATA_FOUND, rc, szSQLFETCH);

if(ind != cTypes + 1)
DISPLAYERROR(szSQLFETCH, TEXT("Incorrect number of result rows"));

rc = SQLFreeStmt(hstmt, SQL_CLOSE);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

rc = SQLFreeStmt(hstmt, SQL_UNBIND);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

} // RetrieveData()



//-----------------------------------------------------------------------
// Function: TestData
//-----------------------------------------------------------------------

void PASCAL TestData(QTSTRUCT *lpqt,FIELDINFO *rgFields,
UWORD cTypes,DSTRUCT *lpd)
{

/* bind all fields to a variable of the correct type for retreiving data */
if (!BindAllColumns(lpqt,rgFields,cTypes,lpd))
return;

SelectFromTable(lpqt);

RetrieveData(lpqt,rgFields,cTypes,lpd);

} // TestData()


//-----------------------------------------------------------------------
// Function: CreateParamQuery
//-----------------------------------------------------------------------

void PASCAL CreateParamQuery(QTSTRUCT *lpqt, FIELDINFO *rgFields,UWORD cTypes)
{
RETCODErc=SQL_SUCCESS;
UWORDi;
LPTSTRpch=NULL;
TCHARszQuery[MAX_QUERY_SIZE];
SDWORDsdw = SQL_DATA_AT_EXEC;
UWORDw,
ind;


wsprintf(szQuery,TEXT("select %s from %s where "), lpqt->szColNames,lpqt->szTableName);


for(i = 0, w = 0,ind=0; i < cTypes; i++)
{
pch = qtMakeData(ind, i,&rgFields[i], lpqt->szDataItem);

if ((rgFields[i].fSearchable == SQL_SEARCHABLE ||
rgFields[i].fSearchable == SQL_ALL_EXCEPT_LIKE) &&
pch && *pch)
{
w++;

switch(rgFields[i].wSQLType)
{
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
wsprintf(&szQuery[lstrlen(szQuery)],TEXT("%s < ? + 1 and "),
rgFields[i].szFieldName);
break;
default:
wsprintf(&szQuery[lstrlen(szQuery)],TEXT("%s = ? and "),
rgFields[i].szFieldName);
}

if(!fBindParameter)
{
rc = SQLSetParam(hstmt, w,
SQL_C_TCHAR, rgFields[i].wSQLType,
rgFields[i].precision, rgFields[i].scale,
(LPTSTR)(UDWORD)i, &sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLSETPARAM);
}
else
{
rc = SQLBindParameter(hstmt, w, SQL_PARAM_INPUT,
SQL_C_TCHAR, rgFields[i].wSQLType,
rgFields[i].precision, rgFields[i].scale,
(LPTSTR)((UDWORD)i+BIND_PARM_OFFSET), 0, &sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLBINDPARAMETER);
}

ind++;
}
}

if (_tcsstr(szQuery,TEXT("and")))
{
/* remove the final "and " */
szQuery[lstrlen(szQuery) - 5] = TEXT('\0');
}

lstrcpy(lpqt->szParamQuery, szQuery);

}

//-----------------------------------------------------------------------
// Function: TestSearchedQuery
//-----------------------------------------------------------------------

void PASCAL TestSearchedQuery(QTSTRUCT *lpqt,FIELDINFO *rgFields,
UWORD cTypes,DSTRUCT *lpd,BOOL fBindParameter)
{
RETCODErc=SQL_SUCCESS;
UWORDi;
LPTSTRpch=NULL;
SDWORDsdw;
UWORDw;
UDWORDudw;
UWORDind;


wsprintf(lpqt->sz, TEXT("select %s from %s where "), lpqt->szColNames,
lpqt->szTableName);

sdw = SQL_DATA_AT_EXEC;

for(i = 0, w = 0,ind=0; i < cTypes; i++)
{
pch = qtMakeData(ind, i,&rgFields[i], lpqt->szDataItem);

if ((rgFields[i].fSearchable == SQL_SEARCHABLE ||
rgFields[i].fSearchable == SQL_ALL_EXCEPT_LIKE) &&
pch && *pch) {
w++;
switch(rgFields[i].wSQLType) {
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
wsprintf(&lpqt->sz[lstrlen(lpqt->sz)], TEXT("%s < ? + 1 and "),
rgFields[i].szFieldName);
break;
default:
wsprintf(&lpqt->sz[lstrlen(lpqt->sz)], TEXT("%s = ? and "),
rgFields[i].szFieldName);
}

if(!fBindParameter){
rc = SQLSetParam(hstmt, w,
SQL_C_TCHAR, rgFields[i].wSQLType,
rgFields[i].precision, rgFields[i].scale,
(LPTSTR)(UDWORD)i, &sdw);
RETCHECK(SQL_SUCCESS, rc, szSQLSETPARAM);
}
else{
rc = SQLBindParameter(hstmt, w, SQL_PARAM_INPUT,
SQL_C_TCHAR, rgFields[i].wSQLType,
rgFields[i].precision, rgFields[i].scale,
(LPTSTR)((UDWORD)i+BIND_PARM_OFFSET), 0, &sdw);
RETCHECK(SQL_SUCCESS, rc, szSQLBINDPARAMETER);
}

ind++;
}
}
/* remove the final "and " */
lpqt->sz[lstrlen(lpqt->sz) - 5] = TEXT('\0');
lstrcpy(lpqt->szParamQuery, lpqt->sz);

rc = SQLPrepare(hstmt, lpqt->sz, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc, szSQLPREPARE);

rc = SQLExecute(hstmt);
RETCHECK(SQL_NEED_DATA, rc, szSQLEXECUTE);

udw = cTypes;

for(i = 0; ; i++)
{
rc = SQLParamData(hstmt, (PTR *)&udw);
if(rc != SQL_NEED_DATA)
break;

if(udw-BIND_PARM_OFFSET < cTypes)
pch = qtMakeData(cTypes, i,&rgFields[udw-BIND_PARM_OFFSET], lpqt->szDataItem);
else
DISPLAYERROR(szSQLPARAMDATA, TEXT("invalid rgbValue"));

if(*pch)
{
udw = lstrlen(pch);
rc = SQLPutData(hstmt, pch, (SDWORD)lstrlen(pch));
RETCHECK(SQL_SUCCESS, rc, szSQLPUTDATA);
}
else
{
rc = SQLPutData(hstmt, (LPTSTR)IGNORED, SQL_NULL_DATA);
RETCHECK(SQL_SUCCESS, rc, szSQLPUTDATA);
}
}

RETCHECK(SQL_SUCCESS, rc, szSQLPARAMDATA);

for(i = 0;; i++)
{
rc = SQLFetch(hstmt);

if(rc != SQL_SUCCESS)
break;
}

RETCHECK(SQL_NO_DATA_FOUND, rc, szSQLFETCH);

/* should have gotten 1 row back */

if(i != 1)
DISPLAYERROR(TEXT("Param/PutData"), TEXT("incorrect number of rows returned"));

FreeStmt(SQL_CLOSE);

} //TestSearchedQuery()



//-----------------------------------------------------------------------
// Function: TestLargeQuery
//-----------------------------------------------------------------------

void PASCAL TestLargeQuery(QTSTRUCT *lpqt,FIELDINFO *rgFields,
UWORD cTypes,DSTRUCT *lpd)
{
RETCODErc=SQL_SUCCESS;
UWORDind=0,
i;
LPTSTRpch=NULL;
TCHARszQuery[2048];


/* > 1K query */
wsprintf(szQuery,TEXT("select %s from %s where "), lpqt->szColNames,
lpqt->szTableName);

for(i = 0; i < cTypes; i++)
{
if (rgFields[i].fSearchable == SQL_SEARCHABLE ||
rgFields[i].fSearchable == SQL_ALL_EXCEPT_LIKE)
break;
}

pch = qtMakeData(cTypes, 1, &rgFields[i], lpqt->szDataItem);
while(lstrlen(szQuery) < 1024L)
{
int li=lstrlen(szQuery);
switch(rgFields[i].wSQLType)
{
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
wsprintf(&szQuery[lstrlen(szQuery)],TEXT("%s < %s + 1 and "),
rgFields[i].szFieldName, pch);
break;
default:
wsprintf(&szQuery[lstrlen(szQuery)],TEXT("%s = %s%s%s and "),
rgFields[i].szFieldName, rgFields[i].szPrefix, pch, rgFields[i].szSuffix);
break;
}
}

/* remove the final "and " */
szQuery[lstrlen(szQuery) - 5] = TEXT('\0');

rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

if (RC_SUCCESSFUL(rc))
{
for(i = 0;; i++)
{
rc = SQLFetch(hstmt);

if(rc != SQL_SUCCESS)
break;
}

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

/* should have gotten at least 1 row back */

if(i < 1)
DISPLAYERROR(TEXT("> 1K query"),TEXT("incorrect number of rows returned"));

}

/* SQLFreeStmt with SQL_CLOSE to re-use the hstmt */

FreeStmt(SQL_CLOSE);

} //TestLargeQuery()



//-----------------------------------------------------------------------
// Function: TestSQLTables
//-----------------------------------------------------------------------

void PASCAL TestSQLTables(QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
BOOLfFoundTable=FALSE;
UWORDi;

/* this call may return many tables, as */
/* long as the one created earlier shows */
/* up it will pass. */

rc = SQLTables(hstmt, NULL, 0, NULL, 0,TEXT("q%"),SQL_NTS,TEXT("'TABLE'"), SQL_NTS);

RETCHECK(SQL_SUCCESS, rc,szSQLTABLES);

for(i = 0;; i++)
{
rc = SQLFetch(hstmt);
if(rc != SQL_SUCCESS)
break;

/* column 3 is tablename */
rc = SQLGetData(hstmt, 3, SQL_C_TCHAR, lpqt->sz, MAX_STRING_SIZE, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA); /* should not overflow
and return SQL_SUCCESS_WITH_INFO because the buffer is larger than the
table name */
fFoundTable += 0 == lstrcmpi(lpqt->sz, lpqt->szTableName);
}

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

if(1 != fFoundTable) {
DISPLAYERROR(szSQLTABLES,TEXT("table not found"));
}

FreeStmt(SQL_CLOSE);

} //TestSQLTables()


//-----------------------------------------------------------------------
// Function: TestSQLSpecialCols
//-----------------------------------------------------------------------

void PASCAL TestSQLSpecialCols(QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
UWORDi;

rc = SQLSpecialColumns(hstmt, SQL_BEST_ROWID, NULL, 0, NULL, 0,
lpqt->szTableName, (SWORD)(lstrlen(lpqt->szTableName)), SQL_SCOPE_TRANSACTION,
SQL_NULLABLE);
RETCHECK(SQL_SUCCESS, rc,szSQLSPECIALCOLUMNS);

for(i = 0;; i++)
{
rc = SQLFetch(hstmt);
if(rc != SQL_SUCCESS)
break;
}

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

FreeStmt(SQL_CLOSE);

} //TestSQLSpecialCols()




//-----------------------------------------------------------------------
// Function: VerifyIndex
//-----------------------------------------------------------------------

void PASCAL VerifyIndex(QTSTRUCT *lpqt,UWORD fIndex)
{
RETCODErc=SQL_SUCCESS;
BOOLfFoundTable=FALSE;
UWORDi;
SDWORDsdw;

rc = SQLStatistics(hstmt, NULL, 0, NULL, 0, lpqt->szTableName,
SQL_NTS, SQL_INDEX_ALL, SQL_ENSURE);
RETCHECK(SQL_SUCCESS, rc,TEXT("SQLStatistics"));

fFoundTable = 0;
for(i = 0;; i++)
{
rc = SQLFetch(hstmt);
if(rc != SQL_SUCCESS)
break;

rc = SQLGetData(hstmt, 3, SQL_C_TCHAR, lpqt->sz, MAX_STRING_SIZE,
&sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);
if (lstrcmpi(lpqt->sz, lpqt->szTableName) == 0)
{
rc = SQLGetData(hstmt, 6, SQL_C_TCHAR, lpqt->sz, MAX_STRING_SIZE,
&sdw);
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);
fFoundTable += 0 == lstrcmpi(lpqt->sz, lpqt->szDataItem);
}
}

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

if(1 != fFoundTable && fIndex)
DISPLAYERROR(TEXT("SQLStatistics"),TEXT("index not returned"));

/* one row represents original table, the */
/* other represents the index */
if(i > 2 || i < 1)
DISPLAYERROR(TEXT("SQLStatistics"),TEXT("too many rows"));

FreeStmt(SQL_CLOSE);

} //VerifyIndex()

//-----------------------------------------------------------------------
// Function: TestSQLStatistics
//-----------------------------------------------------------------------

BOOL PASCAL TestSQLStatistics(QTSTRUCT *lpqt,FIELDINFO *rgFields,
UWORD cTypes,DSTRUCT *lpd,UWORD fIndex)
{
RETCODErc=SQL_SUCCESS;
UWORDi;

lstrcpy(lpqt->szDataItem, lpqt->szTableName);
lpqt->szDataItem[0] = TEXT('i');
for(i = 1; i < cTypes; i++)
if(rgFields[i].wSQLType == SQL_INTEGER ||
rgFields[i].wSQLType == SQL_SMALLINT)
break;
if(i == cTypes)
i = 0;

lstrcpy(lpqt->buf, rgFields[i].szFieldName);
wsprintf(lpqt->sz,TEXT("create unique index %s on %s (%s)"),
lpqt->szDataItem, lpqt->szTableName, lpqt->buf);

rc = SQLExecDirect(hstmt, lpqt->sz, SQL_NTS);
if(fIndex < 1)
/* if this is minimal grammar, don't count on indexes being available */
fIndex = rc == SQL_SUCCESS;
else
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

VerifyIndex(lpqt,fIndex);

return(fIndex);

} //TestSQLStatistics()


//-----------------------------------------------------------------------
// Function: TestLikeQuery
//-----------------------------------------------------------------------

void PASCAL TestLikeQuery(QTSTRUCT *lpqt,FIELDINFO *rgFields,
UWORD cTypes,DSTRUCT *lpd)
{
RETCODErc=SQL_SUCCESS;
UWORDi;
SDWORDcbValue=0;

for(i = 0; i < cTypes; i++)
{
if(rgFields[i].fSearchable == SQL_LIKE_ONLY ||
rgFields[i].fSearchable == SQL_SEARCHABLE)
{
lstrcpy(lpqt->buf, rgFields[i].szFieldName);
break;
}
}

if(i < cTypes)
{

/* execute a query using like. This query should return all records */
/* this query should return all rows in the table */

wsprintf(lpqt->sz,TEXT("select * from %s where %s not like 'a'"), lpqt->szTableName,
lpqt->buf, lpqt->buf);
rc = SQLExecDirect(hstmt, lpqt->sz, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

for(i = 0;; i++)
{
rc = SQLFetch(hstmt);
if(rc != SQL_SUCCESS)
break;

rc = SQLGetData(hstmt, 1, SQL_C_TCHAR, lpqt->sz, MAX_STRING_SIZE,
&cbValue);
if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);
}

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

if(i != cTypes)
DISPLAYERROR(TEXT("'LIKE' query"),TEXT("incorrect number of result rows"));

}

FreeStmt(SQL_CLOSE);

} //TestLikeQuery()


//-----------------------------------------------------------------------
// Function: TestLikeQuery
//-----------------------------------------------------------------------

void PASCAL TestOJCap(QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
UDWORDudw;

rc = SQLGetInfo(hdbc, (UWORD)SQL_OJ_CAPABILITIES, &udw, (SWORD)(sizeof(udw)), NULL);
if(RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO))
{
udw &= ~(SQL_OJ_LEFT | SQL_OJ_RIGHT | SQL_OJ_FULL | SQL_OJ_NESTED|
SQL_OJ_NOT_ORDERED | SQL_OJ_INNER | SQL_OJ_ALL_COMPARISON_OPS);
if(udw)
{
wsprintf(lpqt->buf,TEXT("Undefined flags return from SQLGetInfo(...SQL_OJ_CAPABILITIES...) = %lX"), udw);
DISPLAYERROR(szSQLGETINFO, lpqt->buf);
}
}

rc = SQLGetInfo(hdbc, SQL_OUTER_JOINS, lpqt->buf, MAX_STRING_SIZE, NULL);
if(RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO)){
*lpqt->buf = toupper(*lpqt->buf);
if((!_tcsnccmp(lpqt->buf,TEXT("Y"), 1)) && (!_tcsnccmp(lpqt->buf,TEXT("N"), 1)))
DISPLAYERROR(szSQLGETINFO,TEXT("Driver returned an invalid value for SQLGetInfo(...SQL_OUTER_JOINS...). The only valid values are \"Y\" and \"N\"."));
}

} //TestOJCap()


/*-----------------------------------------------------------------------
* Function: TestExtendedFetch
*-----------------------------------------------------------------------*/

void PASCAL TestExtendedFetch(QTSTRUCT *lpqt, FIELDINFO *rgFields, UWORD cTypes)
{
RETCODErc=SQL_SUCCESS;
WORDfLevel2 = FALSE;
DWORDdwLen=0;
SWORDfSqlType;
SDWORDcbValue=0;
UWORDiTableCol,row,iCol,uwRowSetSize;
TCHARszQuery[MAX_STRING_SIZE], szColName[SMALLBUFF];
TCHAR*rgbValue;
DSTRUCT*rgData;
DSTRUCT*pDataBuf;
BOOLfCol0Bound=TRUE;

uwRowSetSize=5; /* Cinco seems like a good rowset size */

/* Only col which might not be bound but might not be AutoUpdateable is col 0 */
if(!qtMakeData(0, 0, &rgFields[0], szQuery))
fCol0Bound = FALSE;

SelectFromTable(lpqt);

rc = SQLBindCol(hstmt, 1, SQL_C_BINARY, lpqt->sz, MAX_BIND_ARRAY_ELEMENT,
&cbValue);
RETCHECK(SQL_SUCCESS, rc,szSQLBINDCOL);

if (!Supported(SQL_API_SQLEXTENDEDFETCH))
{
rc = SQLExtendedFetch(hstmt, SQL_FETCH_FIRST, IGNORED, NULL, NULL);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLEXTENDEDFETCH, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLEXTENDEDFETCH);

FreeStmt(SQL_CLOSE);
return;
}

rc = SQLGetStmtOption(hstmt,SQL_CURSOR_TYPE,&dwLen);
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTOPTION);

if(dwLen != SQL_CURSOR_FORWARD_ONLY)
{
rc = SQLExtendedFetch(hstmt, SQL_FETCH_FIRST, IGNORED, NULL, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLEXTENDEDFETCH);
}
else
{
rc = SQLExtendedFetch(hstmt, SQL_FETCH_FIRST, IGNORED, NULL, NULL);
RETCHECK(SQL_ERROR, rc,szSQLEXTENDEDFETCH);
}

rc = SQLFreeStmt(hstmt, SQL_CLOSE);

RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT); 

rc = SQLFreeStmt(hstmt, SQL_UNBIND);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

rc = SQLFreeStmt(hstmt, SQL_RESET_PARAMS);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

/* Allocate an area for ExtendedFetch to take place */
if (!(rgData = AllocateMemory(sizeof(DSTRUCT)*cTypes*uwRowSetSize))){
DISPLAYERROR(szSQLEXTENDEDFETCH,TEXT("Insufficient Memory Available"));
return;
}

/* Allocate an area for test data */
if (!(rgbValue = AllocateMemory(MAX_STRING_SIZE))){
DISPLAYERROR(szSQLEXTENDEDFETCH,TEXT("Insufficient Memory Available"));
ReleaseMemory(rgData);
return;
}

/* Get column for "order by" clause: */
wsprintf(szQuery,TEXT("select %s from %s"),lpqt->szColNames, lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
if((SQL_SUCCESS!=rc) && (SQL_SUCCESS_WITH_INFO!=rc)){
RETCHECK(SQL_SUCCESS_WITH_INFO, rc, szSQLEXECDIRECT);
goto abortxfetch;
}
iCol=0;
do{
iCol++;
rc=SQLDescribeCol(hstmt, iCol, szColName, (SMALLBUFF*sizeof(TCHAR)),
NULL, &fSqlType, NULL, NULL, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLDESCRIBECOL);
} while(fSqlType!=SQL_CHAR && iCol<=cTypes);

FreeStmt(SQL_CLOSE);
if(iCol>cTypes){
DISPLAYERROR(szSQLEXTENDEDFETCH, TEXT("\t\t\tSelect-order by Failed"));
goto abortxfetch;
}

/* Set rowset size and set to row-wise binding: */
rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, (UDWORD)uwRowSetSize);
if (SQL_SUCCESS != rc){
DISPLAYERROR(TEXT("ExtendedFetch...SQLSetStmtOption(Rowset_Size)"),
TEXT("SQLSetStmtOption Failed"));
GetErrors(henv, hdbc,hstmt);
}
rc = SQLSetStmtOption(hstmt, SQL_BIND_TYPE, (sizeof(DSTRUCT)*cTypes));
if (SQL_SUCCESS != rc){
DISPLAYERROR(TEXT("ExtendedFetch...SQLSetStmtOption(Bind_Type)"),
TEXT("SQLSetStmtOption Failed"));
GetErrors(henv, hdbc,hstmt);
}

if(SQL_SUCCESS != SelectFromTableFetch(lpqt, szColName)){
DISPLAYERROR(szSQLEXTENDEDFETCH, TEXT("\t\t\tSelect-order by Failed"));
goto abortxfetch;
}
if (!BindFetchColumns(lpqt, rgFields, cTypes, rgData)){
DISPLAYERROR(szSQLEXTENDEDFETCH, szSQLBINDCOL);
goto abortxfetch;
}
/* Fetch into allocated area rowset to be tested */
rc = SQLExtendedFetch(hstmt, SQL_FETCH_NEXT, 1, NULL, NULL);
if (SQL_SUCCESS != rc){
RETCHECK(SQL_SUCCESS, rc,szSQLEXTENDEDFETCH);
DISPLAYERROR(szSQLEXTENDEDFETCH, TEXT("Fetch Failed"));
goto abortxfetch;
}

rc = SQLFreeStmt(hstmt, SQL_CLOSE);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

rc = SQLFreeStmt(hstmt, SQL_UNBIND);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

/* Set rowset size to "1" to test each row of rowset independently: */
rc = SQLSetStmtOption(hstmt, SQL_ROWSET_SIZE, (UDWORD)(1));
if (SQL_SUCCESS != rc){
DISPLAYERROR(TEXT("ExtendedFetch...SQLSetStmtOption(Rowset_Size)"),
TEXT("SQLSetStmtOption Failed"));
GetErrors(henv, hdbc,hstmt);
}

/* Check data in all rows of rowset */
if(SQL_SUCCESS != SelectFromTableFetch(lpqt, szColName)){
DISPLAYERROR(szSQLEXTENDEDFETCH, TEXT("\t\t\tSelect-order by Failed"));
goto abortxfetch;
}

// Need to change this loop if number of rows in table < uwRowSetSize
for (row=0; row<uwRowSetSize; row++)
{
SWORD iResultSetCol=0;
if (SQL_SUCCESS != (rc = SQLFetch(hstmt))){
RETCHECK(SQL_SUCCESS, rc, szSQLFETCH);
DISPLAYERROR(szSQLEXTENDEDFETCH, TEXT("Fetch Failed"));
goto abortxfetch;
}

pDataBuf=rgData+row*cTypes;
for (iTableCol=0; iTableCol<cTypes; iTableCol++)
{
/* Ignore columns which aren't bound as they aren't in the result set */
if((!iTableCol && !fCol0Bound) || rgFields[iTableCol].fAutoUpdate)
continue;

iResultSetCol++;
rc = SQLGetData(hstmt, iResultSetCol, SQL_C_TCHAR, rgbValue, MAX_STRING_SIZE,
&cbValue);
if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);

if(!CheckDataResults(row, iTableCol, &rgFields[iTableCol], pDataBuf,cbValue,rgbValue))
{
wsprintf(lpqt->buf, TEXT("Data Check Failed, Row %d, Table Col %d, DataType %d (%s)"),
row, iTableCol, rgFields[iTableCol].wSQLType, rgFields[iTableCol].szFieldName) ;
szWrite(lpqt->buf,FALSE);
rgbValue[MAX_STRING_SIZE-1]=TEXT('\0');
wsprintf(lpqt->buf, TEXT("Returned Data: (%s)"), rgbValue);
DISPLAYERROR(TEXT("ExtendedFetch"),lpqt->buf);
// goto abortxfetch;
}
}
}

abortxfetch:
FreeStmt(SQL_CLOSE);
FreeStmt(SQL_UNBIND);
ReleaseMemory(rgData);
ReleaseMemory(rgbValue);
} /* TestExtendedFetch() */




/*------------------------------------------------------------------------------
* Function: TestQuickRebind Feature
* Purpose: To test this specific feature, not the function(s) using it.
* Synopsis:
* An area large enough for 12 rows of QRDSTRUCTs will be created. There will
* be two rows of the result set per Rowset. Rowset 1 should be duplicated
* in rows 0,1 and 3,4, while Rowset 2 should be duplicated in rows 6,7 and 9,10.
* Thus, rows 0 and 3 will contain the first row of the result set, rows 1 and
* 4 will contain the second row of the result set, etc. Rows 2, 5, 8, and
* 11 should have their preset condition of all zeroes remain unchanged.
* ----------------------------------------------------------------------------*/

void PASCAL TestQuickRebind(QTSTRUCT * lpqt, FIELDINFO *rgFields, UWORD cTypes,
DSTRUCT *lpd)
{
RETCODErc;
UWORDrow,iCol,iRowset,uwRowsetSize,iRowsetRow,iTableCol;
UDWORDudwRowSize;
SWORDfSqlType;
SDWORDsdwOffset;
DSTRUCT*rgData, *pZeroRow;
DSTRUCT*pCurrentRowData;
TCHARszQuery[MAX_STRING_SIZE], szColName[SMALLBUFF];
SDWORDcbValue;
TCHAR*rgbValue=NULL;
BOOLfCol0Bound=TRUE;

udwRowSize = sizeof(DSTRUCT)*cTypes;
/* cNumResSetCols is set in BuildInsertStmt() */
uwRowsetSize = 5;/* uwRowsetSize rows per Rowset */

/* Only col which might not be bound but might not be AutoUpdateable is col 0 */
if(!qtMakeData(0, 0, &rgFields[0], szQuery))
fCol0Bound = FALSE;

/* Allocate an area for QuickRebind to take place */
if (!(rgData = AllocateMemory(NUM_QUIKREBIND_ROWSETS*(uwRowsetSize+1)*udwRowSize) )){
DISPLAYERROR(szQUICKREBIND,TEXT("\t\t\tInsufficient Memory Available"));
return;
}

/* Allocate an area for a row of zeroes to place between each Rowset. */
if (!(pZeroRow = AllocateMemory(udwRowSize) )){
DISPLAYERROR(szQUICKREBIND,TEXT("\t\t\tInsufficient Memory Available"));
goto abortQuikRebind;
}

/* Allocate an area for test data */
if (!(rgbValue = AllocateMemory(MAX_STRING_SIZE))){
DISPLAYERROR(szSQLEXTENDEDFETCH,TEXT("Insufficient Memory Available"));
goto abortQuikRebind;
}

ResetHstmt(&hstmt);

/* Get column for "order by" clause: */
wsprintf(szQuery,TEXT("select %s from %s"),lpqt->szColNames, lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
if((SQL_SUCCESS!=rc) && (SQL_SUCCESS_WITH_INFO!=rc)){
RETCHECK(SQL_SUCCESS_WITH_INFO, rc, szSQLEXECDIRECT);
goto abortQuikRebind;
}
iCol=0;
do{
iCol++;
rc=SQLDescribeCol(hstmt, iCol, szColName, (SMALLBUFF*sizeof(TCHAR)),
NULL, &fSqlType, NULL, NULL, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLDESCRIBECOL);
} while(fSqlType!=SQL_CHAR && iCol<=cTypes);

FreeStmt(SQL_CLOSE);
if(iCol>=cTypes){
DISPLAYERROR(szQUICKREBIND, TEXT("\t\t\tSelect-order by Failed"));
goto abortQuikRebind;
}

/* Set to row-wise binding and set Rowset size to uwRowsetSize: */
rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_BIND_TYPE,(PTR)udwRowSize,SQL_IS_INTEGER);
if (SQL_SUCCESS != rc){
_tcscpy(lpqt->buf,TEXT("\t\t\tQuick Rebind FAILED...SQLSetDescField(Bind_Type)"));
szWrite(lpqt->buf, TRUE);
goto abortQuikRebind;
}
rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(PTR)uwRowsetSize,SQL_IS_INTEGER);
if (SQL_SUCCESS != rc){
DISPLAYERROR(szQUICKREBIND,TEXT("\t\t\tSetStmtAttr(Rowset Size)"));
goto abortQuikRebind;
}

/* Define area storing Quikrebind offset value: */
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_OFFSET_PTR, &sdwOffset, SQL_IS_POINTER);
if (SQL_SUCCESS != rc){
if(FindError(SQL_HANDLE_STMT,szHYC00)){
_tcscpy(lpqt->buf,TEXT("\t\t\tQuickRebind Not Supported"));
szWrite(lpqt->buf, TRUE);
goto abortQuikRebind;
}
_tcscpy(lpqt->buf,TEXT("\t\t\tQuick Rebind FAILED...QuickRebind Not Functional"));
szWrite(lpqt->buf, TRUE);
goto abortQuikRebind;
}

if(SQL_SUCCESS != SelectFromTableFetch(lpqt, szColName)){
DISPLAYERROR(szQUICKREBIND, TEXT("\t\t\tSelect-order by Failed"));
goto abortQuikRebind;
}

if (!BindFetchColumns(lpqt, rgFields, cTypes, rgData)){
DISPLAYERROR(szQUICKREBIND,szSQLBINDCOL);
goto abortQuikRebind;
}

/* Test quick rebind feature with two pairs of Rowsets, all *
* rowsets separated by a row of zeroes: */
for (iRowset=0; iRowset<=1; iRowset++)
{
if(iRowset)
if(SQL_SUCCESS != SelectFromTableFetch(lpqt, szColName)){
DISPLAYERROR(szQUICKREBIND, TEXT("\t\t\tSelect-order by Failed"));
goto abortQuikRebind;
}

/* Set offset value (to 0 or 1 * uwRowsetSize * udwRowSize) for 1st rowset */
sdwOffset = iRowset*((1+uwRowsetSize)*udwRowSize);

/* Fetch rowset into desired area */
rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 1);
if (SQL_SUCCESS != rc){
ERRSHANDLE(SQL_HANDLE_STMT, hstmt, SQL_SUCCESS, rc, szSQLFETCHSCROLL);
DISPLAYERROR(szQUICKREBIND,TEXT("Fetch Failed"));
goto abortQuikRebind;
}

/* Set offset value (to 2 or 3 * uwRowsetSize * udwRowSize) for 2nd rowset */
sdwOffset = (iRowset+2)*(1+uwRowsetSize)*udwRowSize;

rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 1);
if (SQL_SUCCESS != rc){
ERRSHANDLE(SQL_HANDLE_STMT, hstmt, SQL_SUCCESS, rc, szSQLFETCHSCROLL);
DISPLAYERROR(szQUICKREBIND,TEXT("Fetch Failed"));
goto abortQuikRebind;
}
FreeStmt(SQL_CLOSE);
}

rc = SQLFreeStmt(hstmt, SQL_UNBIND);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

rc = SQLSetStmtAttr(hstmt,SQL_ATTR_ROW_ARRAY_SIZE,(PTR)1,SQL_IS_INTEGER);
if (SQL_SUCCESS != rc){
DISPLAYERROR(szQUICKREBIND,TEXT("\t\t\tSetStmtAttr(Rowset Size)"));
goto abortQuikRebind;
}

/* Check stored values: */
for (iRowset=0; iRowset<=1; iRowset++)
{
if(SQL_SUCCESS != SelectFromTableFetch(lpqt, szColName)){
DISPLAYERROR(szQUICKREBIND, TEXT("\t\t\tSelect-order by Failed"));
goto abortQuikRebind;
}

/* First Rowset starts either in Allocated Row 0 or 3 */
iRowsetRow= iRowset*(1+uwRowsetSize);
//for (row=0; row<NUM_QUIKREBIND_ROWSETS ;row++,iRowsetRow++)
for (row=0; row<(uwRowsetSize*2) ;row++,iRowsetRow++)
{
UWORD iResultSetCol=0;
if(row==uwRowsetSize)
iRowsetRow+= 2+uwRowsetSize; /* Jump to 2nd Rowset */

/* Don't check data in "zero" row, *
* the spacer row between rowsets:*/
if(iRowsetRow%(1+uwRowsetSize) == uwRowsetSize)
continue;

/* Fetch next rowset */
rc = SQLFetch(hstmt);
if (SQL_SUCCESS != rc){
ERRSHANDLE(SQL_HANDLE_STMT, hstmt, SQL_SUCCESS, rc, szSQLFETCH);
DISPLAYERROR(szQUICKREBIND,TEXT("Fetch Failed"));
goto abortQuikRebind;
}

pCurrentRowData = rgData+iRowsetRow*cTypes;
for(iTableCol=0; iTableCol<cTypes; iTableCol++)
{
/* Ignore columns which aren't bound as they aren't in the result set */
if((!iTableCol && !fCol0Bound) || rgFields[iTableCol].fAutoUpdate)
continue;

iResultSetCol++;
rc = SQLGetData(hstmt, iResultSetCol, SQL_C_TCHAR, rgbValue, MAX_STRING_SIZE,
&cbValue);
if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);

if(!CheckDataResults(row, iTableCol, &rgFields[iTableCol], pCurrentRowData,cbValue,rgbValue))
{
wsprintf(lpqt->buf, TEXT("Data Check Failed, Row %d, Table Col %d, DataType %d (%s)"),
row, iTableCol, rgFields[iTableCol].wSQLType, rgFields[iTableCol].szFieldName) ;
szWrite(lpqt->buf,FALSE);
rgbValue[MAX_STRING_SIZE-1]=TEXT('\0');
wsprintf(lpqt->buf, TEXT("Returned Data: (%s)"), rgbValue);
DISPLAYERROR(szQUICKREBIND,lpqt->buf);
goto abortQuikRebind;
}
}/* end for(iTableCol) */
} /* end for(row) */
FreeStmt(SQL_CLOSE);
} /* end for (iRowset) */

/* Check Unused memory areas: */
for (iRowsetRow=uwRowsetSize; iRowsetRow<NUM_QUIKREBIND_ROWSETS*(uwRowsetSize+1); )
{
pCurrentRowData = rgData+iRowsetRow*cTypes;
if(memcmp(pCurrentRowData, pZeroRow, udwRowSize))
DISPLAYERROR(szQUICKREBIND,
TEXT("Unused memory areas altered after Quick Rebind."));
iRowsetRow+=(uwRowsetSize+1);
}


abortQuikRebind:
ResetHstmt(&hstmt);
if (rgbValue)
ReleaseMemory(rgbValue);
if (pZeroRow)
ReleaseMemory(pZeroRow);
ReleaseMemory(rgData);

} /* TestQuickRebind() */

//-----------------------------------------------------------------------
// Function: TestSQLForeignKeys
//-----------------------------------------------------------------------

void PASCAL TestSQLForeignKeys(QTSTRUCT *lpqt)
{
RETCODE rc=SQL_SUCCESS;

if (!Supported(SQL_API_SQLFOREIGNKEYS))
{
rc = SQLForeignKeys(hstmt, NULL, 0, NULL, 0, lpqt->szTableName,
SQL_NTS, NULL, 0, NULL, 0, NULL, 0);

if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLFOREIGNKEYS, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLFOREIGNKEYS);
}
else
{
rc = SQLForeignKeys(hstmt, NULL, 0, NULL, 0, lpqt->szTableName,
SQL_NTS, NULL, 0, NULL, 0, NULL, 0);

RETCHECK(SQL_SUCCESS, rc,szSQLFOREIGNKEYS);

while(rc == SQL_SUCCESS)
rc = SQLFetch(hstmt);

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

FreeStmt(SQL_CLOSE);

}

} //TestSQLForeignKeys()


//-----------------------------------------------------------------------
// Function: TestSQLBrowseConnect
//-----------------------------------------------------------------------

void PASCAL TestSQLBrowseConnect(lpSERVERINFO lpSI)
{
RETCODErc=SQL_SUCCESS;
UWORDcMaxConnections=0;
TCHARszBCString[40];
TCHARszDSN[40];
HDBChdbcb=NULL;

AllocHdbc(&hdbcb);

rc = SQLGetInfo(hdbc, SQL_ACTIVE_CONNECTIONS, &cMaxConnections, sizeof(int), NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

if(cMaxConnections != 1)
{
lstrcpy(szBCString,TEXT("DSN="));
lstrcat(szBCString, lpSI->szValidServer0);

if(lpSI->szValidServer0[0] == 0)
{
rc = SQLGetInfo(hdbc, SQL_DATA_SOURCE_NAME, &szDSN, 40, NULL);
lstrcat(szBCString, szDSN);
}

if(!Supported(SQL_API_SQLBROWSECONNECT))
{
HDBC thdbc=NULL;

rc = SQLBrowseConnect(hdbcb, szBCString, SQL_NTS, NULL, 0, NULL);
thdbc = hdbc;
hdbc = hdbcb;
if(!FindError(SQL_HANDLE_DBC,szIM001))
DISPLAYERROR(szSQLBROWSECONNECT, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLBROWSECONNECT);
hdbc = thdbc;

rc = SQLFreeConnect(hdbcb);
RETCHECK(SQL_SUCCESS, rc,szSQLFREECONNECT);

}
else
{
rc = SQLBrowseConnect(hdbcb, szBCString, SQL_NTS, NULL, 0, NULL);
RETCHECK(SQL_NEED_DATA, rc,szSQLBROWSECONNECT);

rc = SQLDisconnect(hdbcb);
RETCHECK(SQL_SUCCESS, rc,szSQLDISCONNECT);
rc = SQLFreeConnect(hdbcb);
RETCHECK(SQL_SUCCESS, rc,szSQLFREECONNECT);
}
}

} //TestSQLBrowseConnect()


//-----------------------------------------------------------------------
// Function: TestSQLDataSources
//-----------------------------------------------------------------------

void PASCAL TestSQLDataSources()
{
RETCODE rc=SQL_SUCCESS;

if (!Supported( SQL_API_SQLDATASOURCES))
{
rc = SQLDataSources(henv, SQL_FETCH_FIRST, NULL, 0, NULL, NULL, 0, NULL);

if(!FindError(SQL_HANDLE_ENV,szIM001))
DISPLAYERROR(TEXT("SQLDataSources"), szNotSupported);

RETCHECK(SQL_ERROR, rc,TEXT("SQLDataSources"));
}
else
{
rc = SQLDataSources(henv, SQL_FETCH_FIRST, NULL, 0, NULL, NULL, 0, NULL);
RETCHECK(SQL_SUCCESS, rc,TEXT("SQLDataSources"));
}

} //TestSQLDataSources()


//-----------------------------------------------------------------------
// Function: TestSQLDataSources
//-----------------------------------------------------------------------

void PASCAL TestSQLDrivers()
{
RETCODE rc=SQL_SUCCESS;

if(!Supported( SQL_API_SQLDRIVERS))
{
rc = SQLDrivers(henv, SQL_FETCH_FIRST, NULL, 0, NULL, NULL, 0, NULL);
if(!FindError(SQL_HANDLE_ENV,szIM001))
DISPLAYERROR(TEXT("SQLDrivers"), szNotSupported);
RETCHECK(SQL_ERROR, rc,TEXT("SQLDataSources"));
}
else
{
rc = SQLDrivers(henv, SQL_FETCH_FIRST, NULL, 0, NULL, NULL, 0, NULL);
if(rc != SQL_SUCCESS)
RETCHECK(SQL_SUCCESS_WITH_INFO, rc,TEXT("SQLDrivers"));
}

} //TestSQLDrivers()


//-----------------------------------------------------------------------
// Function: TestSQLMoreResults
//-----------------------------------------------------------------------

void PASCAL TestSQLMoreResults(QTSTRUCT *lpqt)
{
RETCODE rc=SQL_SUCCESS;

if(!Supported( SQL_API_SQLMORERESULTS))
{
SelectFromTable(lpqt);

rc = SQLMoreResults(hstmt);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(TEXT("SQLMoreResults"), szNotSupported);
RETCHECK(SQL_ERROR, rc,TEXT("SQLMoreResults"));
}
else
{
SelectFromTable(lpqt);

rc = SQLMoreResults(hstmt);
RETCHECK(SQL_NO_DATA_FOUND, rc,TEXT("SQLMoreResults"));
}

FreeStmt(SQL_CLOSE);

} //TestSQLMoreResults()


//-----------------------------------------------------------------------
// Function: TestSQLMoreResults
//-----------------------------------------------------------------------

void PASCAL TestSQLNativeSQL(QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
TCHARszQuery[XLARGEBUFF];

wsprintf(szQuery,TEXT("select * from %s were 0 > 1"),lpqt->szTableName);

if(!Supported( SQL_API_SQLNATIVESQL))
{
rc = SQLNativeSql(hdbc, szQuery, SQL_NTS, NULL, 0, NULL);
if(!FindError(SQL_HANDLE_DBC,szIM001))
DISPLAYERROR(TEXT("SQLNativeSql"), szNotSupported);
RETCHECK(SQL_ERROR, rc,TEXT("SQLNativeSql"));
}
else
{
rc = SQLNativeSql(hdbc, szQuery, SQL_NTS, NULL, 0, NULL);
RETCHECK(SQL_SUCCESS, rc,TEXT("SQLNativeSql"));
}

FreeStmt(SQL_CLOSE);

} //TestSQLNativeSQL()


//-----------------------------------------------------------------------
// Function: TestSQLDescribeParam
//-----------------------------------------------------------------------

void PASCAL TestSQLDescribeParam(QTSTRUCT *lpqt)
{
RETCODE rc=SQL_SUCCESS;

rc = SQLPrepare(hstmt, lpqt->szParamQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPREPARE);

if(!Supported( SQL_API_SQLDESCRIBEPARAM))
{
rc = SQLPrepare(hstmt, lpqt->szParamQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPREPARE);
rc = SQLDescribeParam(hstmt, 1, NULL, NULL, NULL, NULL);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLDESCRIBEPARAM, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLDESCRIBEPARAM);
}
else
{
rc = SQLPrepare(hstmt, lpqt->szParamQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPREPARE);
rc = SQLDescribeParam(hstmt, 1, NULL, NULL, NULL, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLDESCRIBEPARAM);
}

FreeStmt(SQL_CLOSE);

} //TestSQLDescribeParam()



//-----------------------------------------------------------------------
// Function: TestSQLNumParams
//-----------------------------------------------------------------------

void PASCAL TestSQLNumParams(QTSTRUCT *lpqt)
{
RETCODE rc=SQL_SUCCESS;

rc = SQLPrepare(hstmt, lpqt->szParamQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPREPARE);

if(!Supported( SQL_API_SQLNUMPARAMS))
{
rc = SQLNumParams(hstmt, NULL);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLNUMPARAMS, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLNUMPARAMS);
}
else
{
rc = SQLNumParams(hstmt, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLNUMPARAMS);
}

FreeStmt(SQL_CLOSE);

} //TestSQLNumParams()





//-----------------------------------------------------------------------
// Function: TestSQLParamOptions
//-----------------------------------------------------------------------

void PASCAL TestSQLParamOptions(QTSTRUCT *lpqt)
{
RETCODE rc=SQL_SUCCESS;

rc = SQLPrepare(hstmt, lpqt->szParamQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPREPARE);

if(!Supported( SQL_API_SQLPARAMOPTIONS))
{
rc = SQLParamOptions(hstmt, 1, NULL);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLPARAMOPTIONS, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLPARAMOPTIONS);
}
else
{
rc = SQLParamOptions(hstmt, 1, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLPARAMOPTIONS);
}

FreeStmt(SQL_CLOSE);

} //TestSQLParamOptions()




//-----------------------------------------------------------------------
// Function: TestSQLPrimaryKeys
//-----------------------------------------------------------------------

void PASCAL TestSQLPrimaryKeys(QTSTRUCT *lpqt)
{
RETCODE rc=SQL_SUCCESS;

if (!Supported( SQL_API_SQLPRIMARYKEYS))
{
rc = SQLPrimaryKeys(hstmt, NULL, 0, NULL, 0, lpqt->szTableName, SQL_NTS);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLPRIMARYKEYS, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLPRIMARYKEYS);
}
else
{
rc = SQLPrimaryKeys(hstmt, NULL, 0, NULL, 0, lpqt->szTableName, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPRIMARYKEYS);
while(rc == SQL_SUCCESS)
rc = SQLFetch(hstmt);

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

FreeStmt(SQL_CLOSE);

}

} //TestSQLPrimaryKeys()



//-----------------------------------------------------------------------
// Function: TestSQLProcedureColumns
//-----------------------------------------------------------------------

void PASCAL TestSQLProcedureColumns(QTSTRUCT *lpqt)
{
RETCODE rc=SQL_SUCCESS;

if(!Supported( SQL_API_SQLPROCEDURECOLUMNS))
{
rc = SQLProcedureColumns(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(TEXT("SQLProcedureColumns"), szNotSupported);
RETCHECK(SQL_ERROR, rc,TEXT("SQLProcedureColumns"));
}
else
{
rc = SQLProcedureColumns(hstmt, NULL, 0, NULL, 0, NULL, 0, NULL, 0);
RETCHECK(SQL_SUCCESS, rc,TEXT("SQLProcedureColumns"));
while(rc == SQL_SUCCESS)
rc = SQLFetch(hstmt);

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

FreeStmt(SQL_CLOSE);
}

} //TestSQLProcedureColumns()



//-----------------------------------------------------------------------
// Function: TestSQLProcedures
//-----------------------------------------------------------------------

void PASCAL TestSQLProcedures()
{
RETCODE rc=SQL_SUCCESS;

if(!Supported( SQL_API_SQLPROCEDURES))
{
rc = SQLProcedures(hstmt, NULL, 0, NULL, 0, NULL, 0);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(TEXT("SQLProcedures"), szNotSupported);
RETCHECK(SQL_ERROR, rc,TEXT("SQLProcedures"));
}
else
{
rc = SQLProcedures(hstmt, NULL, 0, NULL, 0, NULL, 0);
RETCHECK(SQL_SUCCESS, rc,TEXT("SQLProcedures"));
while(rc == SQL_SUCCESS)
rc = SQLFetch(hstmt);
RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

FreeStmt(SQL_CLOSE);
}

} //TestSQLProcedures()


//-----------------------------------------------------------------------
// Function: TestSQLTablePrivileges
//-----------------------------------------------------------------------

void PASCAL TestSQLTablePrivileges()
{
RETCODE rc=SQL_SUCCESS;

if(!Supported( SQL_API_SQLTABLEPRIVILEGES))
{
rc = SQLTablePrivileges(hstmt, NULL, 0, NULL, 0, NULL, 0);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(TEXT("SQLTablePrivileges"), szNotSupported);
RETCHECK(SQL_ERROR, rc,TEXT("SQLTablePrivileges"));
}
else
{
rc = SQLTablePrivileges(hstmt, NULL, 0, NULL, 0, NULL, 0);
RETCHECK(SQL_SUCCESS, rc,TEXT("SQLTablePrivileges"));
while(rc == SQL_SUCCESS)
rc = SQLFetch(hstmt);

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

FreeStmt(SQL_CLOSE);
}

} //TestSQLTablePrivileges()



//-----------------------------------------------------------------------
// Function: TestSQLColumnPrivileges
//-----------------------------------------------------------------------

void PASCAL TestSQLColumnPrivileges(QTSTRUCT *lpqt)
{
RETCODE rc=SQL_SUCCESS;

if(!Supported( SQL_API_SQLCOLUMNPRIVILEGES))
{
rc = SQLColumnPrivileges(hstmt, NULL, 0, NULL, 0, lpqt->szTableName, SQL_NTS,
NULL, 0);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(TEXT("SQLColummPrivileges"), szNotSupported);
RETCHECK(SQL_ERROR, rc,TEXT("SQLColumnPrivileges"));
}
else
{
rc = SQLColumnPrivileges(hstmt, NULL, 0, NULL, 0, lpqt->szTableName, SQL_NTS,
NULL, 0);
RETCHECK(SQL_SUCCESS, rc,TEXT("SQLColumnPrivileges"));
while(rc == SQL_SUCCESS)
rc = SQLFetch(hstmt);

RETCHECK(SQL_NO_DATA_FOUND, rc,szSQLFETCH);

FreeStmt(SQL_CLOSE);
}

} // TestSQLColumnPrivileges()


//-----------------------------------------------------------------------
// Function: TestSQLSetScrollOptions
//-----------------------------------------------------------------------

void PASCAL TestSQLSetScrollOptions()
{
RETCODErc=SQL_SUCCESS;
UWORDi,
j;
UDWORDfSupportedOpt;
UDWORDfSupportedCon;
TCHARszState[100]=TEXT(""),
szErrMsg[XLARGEBUFF]=TEXT("");

FreeStmt(SQL_CLOSE);

if(!Supported( SQL_API_SQLSETSCROLLOPTIONS))
{
rc = SQLSetScrollOptions(hstmt, SQL_CONCUR_READ_ONLY, SQL_SCROLL_FORWARD_ONLY,
1);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLSETSCROLLOPTIONS, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLSETSCROLLOPTIONS);
}
else
{
rc = SQLGetInfo(hdbc, SQL_SCROLL_CONCURRENCY, &fSupportedCon, 4, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);
rc = SQLGetInfo(hdbc, SQL_SCROLL_OPTIONS, &fSupportedOpt, 4, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

for(i = 0; i < sizeof(OptionList) / sizeof(SUPPORTOPTINFO); i ++)
{
for(j = 0; j < sizeof(ConcurList) / sizeof(SUPPORTCONCURINFO); j++)
{
if(fSupportedOpt & OptionList[i].Support && fSupportedCon & ConcurList[j].Support)
{
if(!((ConcurList[j].Option == SQL_CONCUR_VALUES) && ((lpSI->vCursorLib ==
SQL_CUR_USE_IF_NEEDED) || (lpSI->vCursorLib == SQL_CUR_USE_ODBC))))
{
rc = SQLSetScrollOptions(hstmt, ConcurList[j].Option,OptionList[i].Option, 1);
RETCHECK(SQL_SUCCESS, rc,szSQLSETSCROLLOPTIONS);
}

}
}
}
}

FreeStmt(SQL_CLOSE);

} //TestSQLSetScrollOptions()


//-----------------------------------------------------------------------------
//Function: TestConnectAttr
//-----------------------------------------------------------------------------

VOID PASCAL TestConnectAttr(HDBC hdbc,SDWORD fAttribute, UDWORD rgbValue,LPTSTR lpAttr,BOOL fReadOnlyAttr)
{
RETCODErc=SQL_SUCCESS;
TCHARszBuff[LARGEBUFF]=TEXT("");
UDWORDpvParam=0;
RETCODErcExp=fReadOnlyAttr ? SQL_ERROR : SQL_SUCCESS;

rc = SQLSetConnectAttr(hdbc,fAttribute,(PTR)rgbValue,sizeof(rgbValue));

if(!FindError(SQL_HANDLE_DBC,szHYC00))
{

RETCHECK(rcExp,rc,szSQLSETCONNECTATTR);

rc = SQLGetConnectAttr(hdbc,fAttribute,&pvParam,sizeof(pvParam),NULL);

if(!FindError(SQL_HANDLE_DBC,szHYC00))
{
RETCHECK(SQL_SUCCESS,rc,szSQLGETCONNECTATTR);

if(!fReadOnlyAttr && (pvParam != rgbValue))
{
wsprintf(szBuff,TEXT("%s returned incorrect value"),lpAttr);
DISPLAYERROR(szSQLGETCONNECTATTR,szBuff);
}
}
else
RETCHECK(SQL_ERROR,rc,szSQLGETCONNECTATTR);
}
else
RETCHECK(SQL_ERROR,rc,szSQLSETCONNECTATTR);

} //TestConnectAttr()


//-----------------------------------------------------------------------------
//Function: TestSQLSetConnectAttr
// This function tests both SQLSetConnectAttr and SQLGetConnectAttr
//-----------------------------------------------------------------------------

void PASCAL TestSQLSetConnectAttr()
{
RETCODE rc = SQL_SUCCESS;
UDWORDpvParam;

UWORDrgbValue; 
BOOLfSupportSetConnectAttr = TRUE;
BOOLfSupportGetConnectAttr = TRUE;

//make sure these APIs are supported before testing
if(!Supported(SQL_API_SQLSETCONNECTATTR))
{
rgbValue = SQL_MODE_READ_WRITE;
fSupportSetConnectAttr = FALSE;
rc = SQLSetConnectAttr(hdbc,SQL_ACCESS_MODE,&rgbValue,sizeof(rgbValue));
if(!FindError(SQL_HANDLE_DBC,szIM001))
DISPLAYERROR(szSQLSETCONNECTATTR,TEXT("did not return Not supported message"));
RETCHECK(SQL_ERROR,rc,szSQLSETCONNECTATTR);
}

if(!Supported(SQL_API_SQLGETCONNECTATTR))
{
fSupportGetConnectAttr = FALSE;
rc = SQLGetConnectAttr(hdbc,SQL_ACCESS_MODE,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_DBC,szIM001))
DISPLAYERROR(szSQLGETCONNECTATTR,TEXT("did not return Not supported message"));
RETCHECK(SQL_ERROR,rc,szSQLGETCONNECTATTR);
}

if(fSupportSetConnectAttr && (!fSupportGetConnectAttr))
{
//test SQL_ACCESS_MODE
rgbValue = SQL_MODE_READ_WRITE;
rc = SQLSetConnectAttr(hdbc,SQL_ACCESS_MODE,&rgbValue,sizeof(rgbValue));
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLSETCONNECTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLSETCONNECTATTR);
//test SQL_AUTOCOMMIT
rgbValue = SQL_AUTOCOMMIT_ON;
rc = SQLSetConnectAttr(hdbc,SQL_AUTOCOMMIT,&rgbValue,sizeof(rgbValue));
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLSETCONNECTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLSETCONNECTATTR);
//test SQL_LOGIN_TIMEOUT
//the choice of 15 seconds for timeout is arbitrary
rgbValue = 15;
rc = SQLSetConnectAttr(hdbc,SQL_LOGIN_TIMEOUT,&rgbValue,sizeof(rgbValue));
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLSETCONNECTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLSETCONNECTATTR);
}

if((!fSupportSetConnectAttr) && fSupportGetConnectAttr)
{
//test SQL_ACCESS_MODE
rc = SQLGetConnectAttr(hdbc,SQL_ACCESS_MODE,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLGETCONNECTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLGETCONNECTATTR);
//test SQL_AUTOCOMMIT
rc = SQLGetConnectAttr(hdbc,SQL_AUTOCOMMIT,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLGETCONNECTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLGETCONNECTATTR);
//test SQL_LOGIN_TIMEOUT
rc = SQLGetConnectAttr(hdbc,SQL_LOGIN_TIMEOUT,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLGETCONNECTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLGETCONNECTATTR);
//SQL_ATTR_AUTO_IPD is a read-only attribute
rc = SQLGetConnectAttr(hdbc,SQL_ATTR_AUTO_IPD,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_DBC,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLGETCONNECTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLGETCONNECTATTR);
}

if(fSupportSetConnectAttr && fSupportGetConnectAttr)
{

//test SQL_ACCESS_MODE
TestConnectAttr(hdbc,SQL_ACCESS_MODE,SQL_MODE_READ_WRITE,TEXT("SQL_ACCESS_MODE"),FALSE);

//test SQL_AUTOCOMMIT
TestConnectAttr(hdbc,SQL_AUTOCOMMIT,SQL_AUTOCOMMIT_ON,TEXT("SQL_AUTOCOMMIT"),FALSE);

//test SQL_LOGIN_TIMEOUT
//the choice of 15 seconds for timeout is arbitrary
TestConnectAttr(hdbc,SQL_LOGIN_TIMEOUT,15,TEXT("SQL_LOGIN_TIMEOUT"),FALSE);

//SQL_ATTR_AUTO_IPD is a read-only attribute
TestConnectAttr(hdbc,SQL_ATTR_AUTO_IPD,15,TEXT("SQL_ATTR_AUTO_IPD"),TRUE);
}

} //TestSQLSetConnectAttr()



//-----------------------------------------------------------------------------
//Function: TestSQLSetStmtAttr
// This function tests both SQLSetStmtAttr and SQLGetStmtAttr
//-----------------------------------------------------------------------------

void PASCAL TestSQLSetStmtAttr()
{
RETCODE rc = SQL_SUCCESS, rc2= SQL_SUCCESS;
UDWORDpvParam;
UWORDrgbValue;
BOOLfSupportSetStmtAttr = TRUE;
BOOLfSupportGetStmtAttr = TRUE;

//make sure these APIs are supported before testing
if(!Supported(SQL_API_SQLSETSTMTATTR))
{
//the choice of 300 for SQL_MAX_LENGTH is arbitrary
rgbValue = 300;
fSupportSetStmtAttr = FALSE;
rc = SQLSetStmtAttr(hstmt,SQL_MAX_LENGTH,&rgbValue,sizeof(rgbValue));
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLSETSTMTATTR,TEXT("did not return Not supported message"));
RETCHECK(SQL_ERROR,rc,szSQLSETSTMTATTR);
}

if(!Supported(SQL_API_SQLGETSTMTATTR))
{
fSupportGetStmtAttr = FALSE;
rc = SQLGetStmtAttr(hstmt,SQL_MAX_LENGTH,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLGETSTMTATTR,TEXT("did not return Not supported message"));
RETCHECK(SQL_ERROR,rc,szSQLGETSTMTATTR);
}

if(fSupportSetStmtAttr && (!fSupportGetStmtAttr))
{
//test SQL_MAX_LENGTH
rgbValue = 300;
rc = SQLSetStmtAttr(hstmt,SQL_MAX_LENGTH,&rgbValue,sizeof(rgbValue));
if(!FindError(SQL_HANDLE_STMT,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLSETSTMTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLSETSTMTATTR);
//test SQL_ATTR_METADATA_ID -- new in 3.0
rgbValue = SQL_FALSE;
rc = SQLSetStmtAttr(hstmt,SQL_ATTR_METADATA_ID,&rgbValue,sizeof(rgbValue));
if(!FindError(SQL_HANDLE_STMT,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLSETSTMTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLSETSTMTATTR);
}

if((!fSupportSetStmtAttr) && fSupportGetStmtAttr)
{
//test SQL_MAX_LENGTH
rc = SQLGetStmtAttr(hstmt,SQL_MAX_LENGTH,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_STMT,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLGETSTMTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLGETSTMTATTR);
//test SQL_ATTR_METADATA_ID -- new in 3.0
rc = SQLGetStmtAttr(hstmt,SQL_ATTR_METADATA_ID,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_STMT,szHYC00))
RETCHECK(SQL_SUCCESS,rc,szSQLGETSTMTATTR);
else
RETCHECK(SQL_ERROR,rc,szSQLGETSTMTATTR);
}

if(fSupportSetStmtAttr && fSupportGetStmtAttr)
{

//test SQL_MAX_LENGTH
rgbValue = 300;
rc = SQLSetStmtAttr(hstmt,SQL_MAX_LENGTH,(PTR)rgbValue,sizeof(rgbValue));
if(!FindError(SQL_HANDLE_STMT,szHYC00))
{
RETCHECK(SQL_SUCCESS,rc,szSQLSETSTMTATTR);
rc = SQLGetStmtAttr(hstmt,SQL_MAX_LENGTH,&pvParam,sizeof(pvParam),NULL);
if(!FindError(SQL_HANDLE_STMT,szHYC00))
{
RETCHECK(SQL_SUCCESS,rc,szSQLGETSTMTATTR);
if(pvParam != 300)
DISPLAYERROR(szSQLGETSTMTATTR,TEXT("SQL_MAX_LENGTH returned incorrect value"));
}
else
RETCHECK(SQL_ERROR,rc,szSQLGETSTMTATTR);
}
else
RETCHECK(SQL_ERROR,rc,szSQLSETSTMTATTR);


//test SQL_ATTR_METADATA_ID -- new in 3.0
rgbValue = SQL_FALSE;
rc = SQLSetStmtAttr(hstmt,SQL_ATTR_METADATA_ID,(PTR)rgbValue,sizeof(rgbValue));
if (!FindError(SQL_HANDLE_STMT,szHYC00) && !FindError(SQL_HANDLE_STMT,szHY092))
RETCHECK(SQL_SUCCESS,rc,szSQLSETSTMTATTR);

rc2 = SQLGetStmtAttr(hstmt,SQL_ATTR_METADATA_ID,&pvParam,sizeof(pvParam),NULL);
if (!FindError(SQL_HANDLE_STMT,szHYC00) && !FindError(SQL_HANDLE_STMT,szHY092))
RETCHECK(SQL_SUCCESS,rc,szSQLGETSTMTATTR);

if (RC_SUCCESSFUL(rc) && RC_SUCCESSFUL(rc2) && pvParam != SQL_FALSE)
DISPLAYERROR(szSQLGETSTMTATTR,TEXT("SQL_ATTR_METADATA_ID returned incorrect value"));
}

} //TestSQLSetStmtAttr()

//--------------------------------------------------------------------------------
// FUNCTION:TestOneThread
// PURPOSE:This function allocates the statement handle, performs simple
//queries and deallocates the statement handle. This function is called
//by testthreading.
//--------------------------------------------------------------------------------
static int TestOneThread(THREAD_STRUCT *ptArg)
{
HENVhenv0 = ptArg->henv;
HDBChdbc0 = ptArg->hdbc;
HSTMThstmt0 = NULL;
//RETCODErc=SQL_SUCCESS;

/* allocate new statment handle */
if (RC_NOTSUCCESSFUL(SQLAllocHandle(SQL_HANDLE_STMT,hdbc0, &hstmt0)))
return(FALSE);

/* perform some simple catalog functions */
if (RC_NOTSUCCESSFUL(SQLStatistics(hstmt0,NULL,SQL_NTS,NULL,SQL_NTS,NULL,SQL_NTS,SQL_INDEX_ALL,SQL_QUICK)))
return(FALSE);

if (RC_NOTSUCCESSFUL(SQLCloseCursor(hstmt0)))
return(FALSE);

/* Get some data */
GetSomeData(hstmt0, ptArg->szTableName, TRUE,ptArg->rgFieldInfo);

if (RC_NOTSUCCESSFUL(SQLGetTypeInfo(hstmt0,SQL_CHAR)))
return(FALSE);

// Cleanup
if (RC_NOTSUCCESSFUL(SQLFreeHandle(SQL_HANDLE_STMT,hstmt0)))
return(FALSE);

return(TRUE);

} //end of TestOneThread

//--------------------------------------------------------------------------------
// FUNCTION:ThreadLoop
// PURPOSE:This is the main loop for the threads, and it's called by
//CreateThread() API.
//--------------------------------------------------------------------------------
DWORD WINAPI ThreadLoop(void *pArg)
{

THREAD_STRUCT *ptArg = (THREAD_STRUCT *)pArg;
int i;
RETCODErc;

for(i=0; i< NUM_THREAD; i++)
TestOneThread(ptArg);

// free connection handle for each thread
rc=SQLDisconnect(ptArg->hdbc);
RETCHECK(SQL_SUCCESS,rc,szSQLDISCONNECT);
rc=SQLFreeHandle(SQL_HANDLE_DBC,ptArg->hdbc);
RETCHECK(SQL_SUCCESS,rc,szSQLFREEHANDLE);

return(0);
} //end of ThreadLoop


//-------------------------------------------------------------------------------
// FUNCTION:TestThreading
// PURPOSE:This function tests the multi-threading capabilities of the
//driver by creating multiple threads that connect to the same data
// source. Once connected, each thread performs simple queries to
//test that the driver handles these queries successfully.
//This function is to be called inside AutoTestFunc() in QuikTest.
//--------------------------------------------------------------------------------
void TestThreading(lpSERVERINFO lpSI,QTSTRUCT *lpqt,FIELDINFO *rgFieldInfo)
{
RETCODE rc=SQL_SUCCESS;
THREAD_STRUCT *pArg;
HANDLE hThread[NUM_THREAD];
DWORD dwThreadId;
UWORD i;
HENVhenv1;
HDBChdbc1;


// allocate memory for the threads
pArg = (THREAD_STRUCT *)AllocateMemory((NUM_THREAD) * sizeof(THREAD_STRUCT));

if(!pArg)
{
szWrite(TEXT("\t\t\tMultiThreading Test interrupted due to error in memory allocation"), TRUE);
return;
}

// only one environment handle will be needed for all threads
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv1);
RETCHECK(SQL_SUCCESS, rc,szSQLALLOCHANDLE);

// Set environment attribute, or we can't allocate connection
rc = SQLSetEnvAttr(henv1, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,
SQL_IS_UINTEGER);
RETCHECK(SQL_SUCCESS, rc,szSQLSETENVATTR);

for (i=0; i < NUM_THREAD; i++ )
{
// allocate connection handle for each thread
rc=SQLAllocHandle(SQL_HANDLE_DBC,henv1, &hdbc1);
RETCHECK(SQL_SUCCESS, rc,szSQLALLOCHANDLE);

// create string to be used in connecting
wsprintf(lpqt->sz,TEXT("dsn=%s;uid=%s;pwd=%s;"),
lpSI->szValidServer0,
lpSI->szValidLogin0,
lpSI->szValidPassword0);

SQLDriverConnect(hdbc1, lpSI->hwnd, lpqt->sz,
SQL_NTS, lpqt->buf, MAX_STRING_SIZE, NULL, SQL_DRIVER_COMPLETE);

// initialize thread structure
pArg[i].henv = henv1;
pArg[i].hdbc = hdbc1;
lstrcpy(pArg[i].szTableName, lpqt->szTableName);
pArg[i].rgFieldInfo=rgFieldInfo[0];

// start thread
hThread[i]= CreateThread(NULL,
0,
ThreadLoop,
&pArg[i],
0,
&dwThreadId);

if(hThread[i]==0)
{
DISPLAYERROR(TEXT("TestThread failed:"),TEXT("cannot create thread"));
break;
}
}

// wait for thread completion
WaitForMultipleObjects(NUM_THREAD,hThread,TRUE,INFINITE);

for(i=0; i<NUM_THREAD; i++ )
CloseHandle(hThread[i]);

SQLFreeHandle(SQL_HANDLE_ENV,henv1);
if (pArg)
ReleaseMemory(pArg);
}//end of TestThreading()


//-----------------------------------------------------------------------
// Function: CreateDescRecord
//-----------------------------------------------------------------------
SWORD CreateDescRecord(SQLHDESC hdesc, SQLHSTMT hstmt, UWORD uwDescType)
{
UDWORD cbColDef;
SWORD swColCount;
SQLRETURN rc;
SWORD fType, fSQLType;
SWORD ibScale;
TCHAR rgBuf[100];

switch(uwDescType) {
case SQL_ATTR_APP_ROW_DESC:
rc = SQLSetDescField(hdesc, 1, SQL_DESC_TYPE, (SQLPOINTER)SQL_C_DEFAULT,
SQL_IS_SMALLINT);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc,szSQLSETDESCFIELD);

if (SQL_SUCCEEDED(rc))
return 1;

rc=SQLBindCol(hstmt, 1, SQL_C_TCHAR, rgBuf, sizeof(rgBuf), NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLBINDCOL);
if (SQL_SUCCEEDED(rc))
return 1;
break;
case SQL_ATTR_IMP_PARAM_DESC:
case SQL_ATTR_APP_PARAM_DESC:
// Select statement has already been done by this time
rc=SQLDescribeCol(hstmt, 1, NULL, 0, NULL, &fSQLType, &cbColDef,
&ibScale, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLDESCRIBECOL);
if (!SQL_SUCCEEDED(rc))
return 0;

if (SQL_ATTR_APP_PARAM_DESC == uwDescType)
fType=SQL_C_TCHAR;
else
fType=fSQLType;

rc = SQLSetDescField(hdesc, 1, SQL_DESC_TYPE, (SQLPOINTER)fType,
SQL_IS_SMALLINT);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc,szSQLSETDESCFIELD);

if (SQL_SUCCEEDED(rc))
return 1;

rc=SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_TCHAR,
fSQLType, cbColDef, ibScale, rgBuf, sizeof(rgBuf), NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLDESCRIBECOL);
if (SQL_SUCCEEDED(rc))
return 1;
break;
case SQL_ATTR_IMP_ROW_DESC:
// Quiktest has already done the select for us
// Need to find out how many cols in result set
rc=SQLNumResultCols(hstmt, &swColCount);
if (SQL_SUCCEEDED(rc))
return(swColCount);
break;
}
return 0;
}//CreateDescRecord

//-----------------------------------------------------------------------
// Function: CheckDescOp
//-----------------------------------------------------------------------
void CheckDescOp(SQLHDESC hdesc, SQLRETURN rc, SQLUSMALLINT uwDescIndex,
SQLSMALLINT swDescRecCount, SQLSMALLINT swDescRec, SQLUSMALLINT iDescField,
SQLINTEGER swUpdateMode)
{
TCHAR * pszAPI, szOp[10];
SQLSMALLINT fValidOp;// Descriptor types operation is valid for

if (DESC_UPDATE_MODE_READ == swUpdateMode)
{
pszAPI=szSQLGETDESCFIELD;
lstrcpy(szOp, TEXT("read"));
fValidOp=rgDescInfo[iDescField].fGettable;
}
else
{
pszAPI=szSQLSETDESCFIELD;
lstrcpy(szOp, TEXT("write"));
fValidOp=rgDescInfo[iDescField].fSettable;
}

// If we're reading beyond the last record, expect SQL_NO_DATA.
if (swDescRec > swDescRecCount && DESC_UPDATE_MODE_READ == swUpdateMode)
{
wsprintf(buf,TEXT("%s: Able to %s descriptor field %s in the %s beyond last record."),
pszAPI, szOp, rgDescInfo[iDescField].szDescFieldName, DescTypes[uwDescIndex].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_NO_DATA, rc, buf);
}
else
{
if (fValidOp & DescTypes[uwDescIndex].uwTypeMask)
{
wsprintf(buf,TEXT("%s: Unable to %s descriptor field %s in the %s"),
pszAPI, szOp, rgDescInfo[iDescField].szDescFieldName, DescTypes[uwDescIndex].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc, buf);
}
else
{
wsprintf(buf,TEXT("%s: Able to %s descriptor field %s in the %s"),
pszAPI, szOp, rgDescInfo[iDescField].szDescFieldName, DescTypes[uwDescIndex].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_ERROR, rc, buf);
}
}
}

//-----------------------------------------------------------------------
//
// Function: CompareDescField
//
// Compares two fields from the given descriptor, or to rgDescInfo if hdesc2
// is NULL. uwDescField contains the entry in rgDescInfo for the desired field
//-----------------------------------------------------------------------

void CompareDescField(SQLHDESC hdesc1, SQLHDESC hdesc2, SQLUSMALLINT uwDescIndex,
SQLSMALLINT swDescRec, SQLSMALLINT swDescRecCount, SQLUSMALLINT uwDescField,
TCHAR * pszAPI)
{
SQLRETURN rc;
SQLTCHAR DescBuf2[MAX_DESC_BUF];
SQLINTEGER cbValue2;

// Make sure we've got a valid descriptor handle for hdesc1
// (hdesc2 is allowed to be NULL)
if (SQL_NULL_HDESC == hdesc1) {
wsprintf(buf,TEXT("\t\tCompareDescriptor: hdesc1 handle was null!"));
DISPLAYERROR(pszAPI, buf);
return;
}

// Read the value from hdesc1
rc = SQLGetDescField(hdesc1, swDescRec, rgDescInfo[uwDescField].uwDescField,
&DescBuf, sizeof(DescBuf), &cbValue);

CheckDescOp(hdesc1, rc, uwDescIndex, swDescRecCount, swDescRec, uwDescField, DESC_UPDATE_MODE_READ);

cbValue2=rgDescInfo[uwDescField].cbValue;

// Get the value to compare to
if (SQL_NULL_HDESC == hdesc2) {

// Compare to rgDescInfo if it's settable
if (rgDescInfo[uwDescField].fSettable & DescTypes[uwDescIndex].uwTypeMask) {

// Copy the set value into DescBuf2. For char fields size is 0
// (unknown), and the new value is actually the pointer to the start
// of the string.
SQLPOINTER pNewvalue = &(rgDescInfo[uwDescField].NewValue);

if (!rgDescInfo[uwDescField].size)
{
pNewvalue = (SQLPOINTER)rgDescInfo[uwDescField].NewValue;
cbValue2= (lstrlen(pNewvalue)+1) * sizeof(TCHAR);
}

memcpy(&DescBuf2, pNewvalue, cbValue2);
}
}
else {

// Compare to hdesc2
rc = SQLGetDescField(hdesc2, swDescRec, rgDescInfo[uwDescField].uwDescField,
&DescBuf2, sizeof(DescBuf2), &cbValue);

// Since hdesc2 is an unassociated hdesc, treat it as an ARD
// (uwDescIndex=0)
CheckDescOp(hdesc2, rc, 0, swDescRecCount, swDescRec, uwDescField, DESC_UPDATE_MODE_READ);
}

// Perform the compare if settable in source desc and settable in
// target desc (application descriptors) when using hdesc2
if (rgDescInfo[uwDescField].fSettable & DescTypes[uwDescIndex].uwTypeMask)
{
if (!hdesc2 || (rgDescInfo[uwDescField].fSettable & DESC_ARD))
{
// IPD field SQL_DESC_TYPE is settable for consitency check, but set
// value can't be retrieved, so skip compare in this case
if (SQL_ATTR_IMP_PARAM_DESC != DescTypes[uwDescIndex].uwDescType ||
SQL_DESC_DATA_PTR != rgDescInfo[uwDescField].uwDescField)
{
if (memcmp(DescBuf, DescBuf2, cbValue2)) {
// Compare error, print message
wsprintf(buf,TEXT("Compare error in %s field %s"),
DescTypes[uwDescIndex].szDescName,rgDescInfo[uwDescField].szDescFieldName);
DISPLAYERROR(pszAPI, buf);
}
}
}
}
}


//-----------------------------------------------------------------------
//
// Function: CheckDescriptor
//
// Checks all fields in all records of given descriptor. If swUpdateMode is
// DESC_UPDATE_MODE_READ then all fields are read, otherwise all updatable
// fields are written.
//-----------------------------------------------------------------------
void CheckDescriptor(SQLHDESC hdesc, SQLINTEGER swUpdateMode, SQLHSTMT hstmt, UWORD uwDescIndex)
{
SQLINTEGER cbValue2;
SQLSMALLINT swDescRec=0, swDescRecCount, swAdditionalRecs=0;
SQLUSMALLINT iDescField, iStartField=0;
SQLRETURN rc;

// Get the descriptor record count.
rc = SQLGetDescField(hdesc, 0, SQL_DESC_COUNT, &swDescRecCount,
sizeof(swDescRecCount), &cbValue);

if (SQL_SUCCESS == rc) {

// In read mode, try to read one past last record
if (swUpdateMode == DESC_UPDATE_MODE_READ)
swAdditionalRecs=1;

// Check all records, including header
for (swDescRec=0; swDescRec <= swDescRecCount+swAdditionalRecs; swDescRec++) {

// Check all fields
for (iDescField=iStartField; iDescField < sizeof(rgDescInfo)/sizeof(rgDescInfo[0]); iDescField++) {

// If we've gone past the header fields, then break out of loop to check record fields
if (0 == swDescRec && !rgDescInfo[iDescField].fHeader) {
iStartField=iDescField;
break;
}

if (DESC_UPDATE_MODE_READ == swUpdateMode)
{
// Read from the field
rc = SQLGetDescField(hdesc, swDescRec, rgDescInfo[iDescField].uwDescField,
&DescBuf, sizeof(DescBuf), &cbValue);

CheckDescOp(hdesc, rc, uwDescIndex, swDescRecCount, swDescRec, iDescField, swUpdateMode);

if (RC_SUCCESSFUL(rc))
{
// Compare cbValue with expected
cbValue2=rgDescInfo[iDescField].cbValue; // For most fields

if (!rgDescInfo[iDescField].size)
cbValue2 = lstrlen((LPTSTR)DescBuf); // For string fields

// Make sure cbValue and cbValue2 agree
if (cbValue != cbValue2)
{
wsprintf(buf,TEXT("Compare error in %s field %s, expected")\
TEXT(" StringLength %d, received %d"), DescTypes[uwDescIndex].szDescName,
rgDescInfo[iDescField].szDescFieldName, cbValue2, cbValue);
DISPLAYERROR(szSQLGETDESCFIELD, buf);
}
}
}
else
{
// Write to the field. For char fields size is 0 (unknown),
// so compute the length of the string
rc = SQLSetDescField(hdesc, swDescRec, rgDescInfo[iDescField].uwDescField,
(SQLPOINTER)rgDescInfo[iDescField].NewValue,
(rgDescInfo[iDescField].size) ? rgDescInfo[iDescField].size :
lstrlen((LPTSTR)rgDescInfo[iDescField].NewValue)*sizeof(TCHAR));

CheckDescOp(hdesc, rc, uwDescIndex, swDescRecCount, swDescRec, iDescField, swUpdateMode);

if (RC_SUCCESSFUL(rc))
CompareDescField(hdesc, NULL, uwDescIndex, swDescRec, swDescRecCount,iDescField, szSQLSETDESCFIELD);
}
}
}
}
else {
// Unable to read record count from descriptor header, can't proceed
wsprintf(buf,TEXT("\t\tUnable to read record count from descriptor header."));
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc, buf);
}
}

//-----------------------------------------------------------------------
//
// Function: CompareDescriptor
//
// Compares two descriptors, or if hdesc2 is NULL compares hdesc1 to rgDescInfo
// updatable columns
//-----------------------------------------------------------------------

void CompareDescriptor(SQLHDESC hdesc1, SQLHDESC hdesc2, UWORD uwDescIndex, TCHAR * pszAPI)
{
SQLSMALLINT swDescRec, swDescRecCount;
SQLUSMALLINT iDescField, iStartField=0;
SQLRETURN rc;

// Make sure we've got a valid descriptor handle for hdesc1
// (hdesc2 is allowed to be NULL)
if (SQL_NULL_HDESC == hdesc1) {
wsprintf(buf,TEXT("\t\thCompareDescriptor: hdesc1 handle was null!"));
DISPLAYERROR(szSQLGETDESCFIELD, buf);
return;
}

// Get the descriptor record count.
rc = SQLGetDescField(hdesc1, 0, SQL_DESC_COUNT, &swDescRecCount,
sizeof(swDescRecCount), &cbValue);

if (SQL_SUCCESS == rc) {

// Check all records, including header
for (swDescRec=0; swDescRec <= swDescRecCount; swDescRec++) {

// Check all fields
for (iDescField=iStartField; iDescField < sizeof(rgDescInfo)/sizeof(rgDescInfo[0]); iDescField++) {

// If we've gone past the header fields, then break out of loop to check record fields
if (0 == swDescRec && !rgDescInfo[iDescField].fHeader) {
iStartField=iDescField;
break;
}

CompareDescField(hdesc1, hdesc2, uwDescIndex, swDescRec, swDescRecCount, iDescField, pszAPI);
}
}
}
else {
// Unable to read record count from descriptor header, can't proceed
wsprintf(buf,TEXT("\t\tUnable to read record count from descriptor header."));
ERRSHANDLE(SQL_HANDLE_DESC, hdesc1, SQL_SUCCESS, rc, buf);
}
}


void CheckDescRecord(SQLHDESC hdesc, SQLSMALLINT swDescRec, SQLSMALLINT fType,
SQLSMALLINT fSubType, SQLINTEGER cbLength, SQLSMALLINT ibPrecision, SQLSMALLINT ibScale,
SQLPOINTER pData, SQLINTEGER *pcbValue, SQLINTEGER *pIndicator, SQLUSMALLINT iDescType)
{
SQLINTEGER Length, * pIndicator2;
PTR pDescPtr;
SQLRETURN rc;
SQLSMALLINT fType2, fSubType2, cbName, Precision, Scale, Nullable;
TCHARszName[MAX_DESC_BUF]=TEXT("");

// Write the record
rc = SQLSetDescRec(hdesc, swDescRec, fType, fSubType, cbLength, ibPrecision,
ibScale, pData, pcbValue, pIndicator);

// Can't set IRD, so it should error
if (SQL_ATTR_IMP_ROW_DESC == DescTypes[iDescType].uwDescType)
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_ERROR, rc, szSQLSETDESCREC);
else
{
wsprintf(buf,TEXT("%s: Unable to write record %d in %s"),
szSQLSETDESCREC, swDescRec, DescTypes[iDescType].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc, buf);
}

if (RC_SUCCESSFUL(rc))
{
// Read it again
rc = SQLGetDescRec(hdesc, swDescRec, szName, sizeof(szName), &cbName, &fType2,
&fSubType2, &Length, &Precision, &Scale, &Nullable);

wsprintf(buf,TEXT("%s: Unable to read record %d in %s"),
szSQLGETDESCREC, swDescRec, DescTypes[iDescType].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc, buf);

if (RC_SUCCESSFUL(rc))
{
// Compare values
if (fType2 != fType)
DISPLAYERROR(szSQLSETDESCREC,TEXT("fType not set correctly"));

if (Precision != ibPrecision)
DISPLAYERROR(szSQLSETDESCREC,TEXT("ibPrecision not set correctly"));

switch(fType)
{
case SQL_DECIMAL:
case SQL_NUMERIC:
case SQL_C_DOUBLE:
case SQL_C_FLOAT:
// Scale is only defined for NUMERIC or DECIMAL types
if (Scale != ibScale)
DISPLAYERROR(szSQLSETDESCREC,TEXT("ibScale not set correctly"));
break;
case SQL_DATETIME:
case SQL_INTEGER:
// Subtype is only valid for DATETIME or INTERVAL types
if (fSubType2 != fSubType)
DISPLAYERROR(szSQLSETDESCREC,TEXT("fSubType not set correctly"));
break;
}

// For application descriptors, compare the Length and get the
// data ptr value to check for accuracy
switch(DescTypes[iDescType].uwDescType)
{
case SQL_ATTR_APP_ROW_DESC:
case SQL_ATTR_APP_PARAM_DESC:

if (Length != cbLength)
DISPLAYERROR(szSQLSETDESCREC,TEXT("cbLength not set correctly"));

rc = SQLGetDescField(hdesc, swDescRec, SQL_DESC_DATA_PTR,
&pDescPtr, sizeof(PTR), &cbValue);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc, szSQLGETDESCFIELD);

if (pDescPtr != pData)
DISPLAYERROR(szSQLSETDESCREC,TEXT("rgbValue not set correctly"));

// Get the octet length ptr value and check for accuracy
rc = SQLGetDescField(hdesc, swDescRec, SQL_DESC_OCTET_LENGTH_PTR,
&pDescPtr, sizeof(PTR), &cbValue);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc, szSQLGETDESCFIELD);

if (pDescPtr != pcbValue)
DISPLAYERROR(szSQLSETDESCREC,TEXT("cbValue not set correctly"));

rc = SQLGetDescField(hdesc, swDescRec, SQL_DESC_INDICATOR_PTR,
&pIndicator2, sizeof(PTR), &cbValue);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc, szSQLGETDESCFIELD);

if (pIndicator2 != pIndicator)
DISPLAYERROR(szSQLSETDESCREC,TEXT("Indicator not set correctly"));

break;
case SQL_ATTR_IMP_ROW_DESC:
case SQL_ATTR_IMP_PARAM_DESC:
break;
default:
DISPLAYERROR(szSQLSETDESCREC,TEXT("Unknown descriptor type."));
}
}
}

}

SQLRETURN DescBindParameter(SQLHSTMT hstmt, SQLUSMALLINT ipar,
SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType,
SQLUINTEGER cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue,
SQLINTEGER cbValueMax, SQLINTEGER *pcbValue)
{
SQLHDESC hdescAPD, hdescIPD;
SQLRETURN rc;

rc = SQLGetStmtAttr(hstmt, SQL_ATTR_APP_PARAM_DESC, &hdescAPD, sizeof(hdescAPD), NULL);
if (!RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR))
return rc;

rc = SQLGetStmtAttr(hstmt, SQL_ATTR_IMP_PARAM_DESC, &hdescIPD, sizeof(hdescIPD), NULL);
if (!RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR))
return rc;

// Parameter type
rc = SQLSetDescField(hdescIPD, ipar, SQL_DESC_PARAMETER_TYPE, (SQLPOINTER)fParamType,
SQL_IS_SMALLINT);// Set ipar and fParamType for IPD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescIPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

// C type
rc = SQLSetDescField(hdescAPD, ipar, SQL_DESC_CONCISE_TYPE, (SQLPOINTER)fCType,
SQL_IS_SMALLINT);// Set ipar and fCType of APD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescAPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

// Sql type
rc = SQLSetDescField(hdescIPD, ipar, SQL_DESC_CONCISE_TYPE, (SQLPOINTER)fSqlType,
SQL_IS_SMALLINT);// Set fSqlType of IPD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescIPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

// Column Size (Length or Precision)
if (fSqlType == SQL_CHAR ||
fSqlType == SQL_VARCHAR ||
fSqlType == SQL_LONGVARCHAR ||
fSqlType == SQL_BINARY ||
fSqlType == SQL_VARBINARY ||
fSqlType == SQL_LONGVARBINARY ||
fSqlType == SQL_TYPE_DATE ||
fSqlType == SQL_TYPE_TIME ||
fSqlType == SQL_TYPE_TIMESTAMP ||
(fSqlType >= SQL_INTERVAL_YEAR &&
fSqlType <= SQL_INTERVAL_MINUTE_TO_SECOND))
{
rc = SQLSetDescField(hdescIPD, ipar, SQL_DESC_LENGTH, (SQLPOINTER)cbColDef,
SQL_IS_UINTEGER);// Set cbColDef (Length) of IPD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescIPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;
}
else if (fSqlType == SQL_DECIMAL ||

fSqlType == SQL_NUMERIC || 
fSqlType == SQL_FLOAT ||
fSqlType == SQL_REAL ||
fSqlType == SQL_DOUBLE)
{
rc = SQLSetDescField(hdescIPD, ipar, SQL_DESC_PRECISION, (SQLPOINTER)cbColDef,
SQL_IS_SMALLINT);// Set cbColDef (Precision) of IPD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescIPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;
}

// Scale
if (fSqlType == SQL_TYPE_DATE ||
fSqlType == SQL_TYPE_TIME ||
fSqlType == SQL_TYPE_TIMESTAMP ||
(fSqlType >= SQL_INTERVAL_YEAR &&
fSqlType <= SQL_INTERVAL_MINUTE_TO_SECOND))
{
switch(fSqlType)
{
case SQL_TYPE_TIME:
case SQL_TYPE_TIMESTAMP:
case SQL_INTERVAL_SECOND:
case SQL_INTERVAL_DAY_TO_SECOND:
case SQL_INTERVAL_HOUR_TO_SECOND:
case SQL_INTERVAL_MINUTE_TO_SECOND:
rc = SQLSetDescField(hdescIPD, ipar, SQL_DESC_PRECISION, (SQLPOINTER)ibScale,
SQL_IS_SMALLINT);// Set scale of IPD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescIPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;
break;
default:
rc = SQLSetDescField(hdescIPD, ipar, SQL_DESC_PRECISION, (SQLPOINTER)0,
SQL_IS_SMALLINT);// Set scale of IPD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescIPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;
}
}
else
{
rc = SQLSetDescField(hdescIPD, ipar, SQL_DESC_SCALE, (SQLPOINTER)ibScale,
SQL_IS_SMALLINT);// Set scale of IPD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescIPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;
}

// cbValueMax
rc = SQLSetDescField(hdescAPD, ipar, SQL_DESC_OCTET_LENGTH, (SQLPOINTER)cbValueMax,
SQL_IS_INTEGER);// Set cbValueMax of APD
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescAPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

// pcbValue
rc = SQLSetDescField(hdescAPD, ipar, SQL_DESC_OCTET_LENGTH_PTR, (SQLPOINTER)pcbValue,
SQL_IS_POINTER);// Set pcbValue/octet length
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescAPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

rc = SQLSetDescField(hdescAPD, ipar, SQL_DESC_INDICATOR_PTR, (SQLPOINTER)pcbValue,
SQL_IS_POINTER);// Set pcbValue/indicator
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdescAPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

// Data ptr
rc = SQLSetDescField(hdescAPD, ipar, SQL_DESC_DATA_PTR, (SQLPOINTER)rgbValue,
SQL_IS_POINTER);// Set rgbValue
ERRSHANDLE(SQL_HANDLE_DESC, hdescAPD, SQL_SUCCESS, rc,szSQLSETDESCFIELD);

return rc;

}

SQLRETURN DescBindCol(SQLHSTMT hstmt, SQLSMALLINT icol,
SQLSMALLINT fCType, SQLPOINTER rgbValue,
SQLINTEGER cbValueMax, SQLINTEGER *pcbValue)
{
SQLHDESC hdesc;
SQLRETURN rc;

rc = SQLGetStmtAttr(hstmt, SQL_ATTR_APP_ROW_DESC, &hdesc, sizeof(hdesc), NULL);
if (!RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR))
return rc;

rc = SQLSetDescField(hdesc, icol, SQL_DESC_CONCISE_TYPE, (SQLPOINTER)fCType,
SQL_IS_SMALLINT);// Set icol and fCType
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

rc = SQLSetDescField(hdesc, icol, SQL_DESC_OCTET_LENGTH, (SQLPOINTER)cbValueMax,
SQL_IS_INTEGER);// Set cbValueMax
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

rc = SQLSetDescField(hdesc, icol, SQL_DESC_OCTET_LENGTH_PTR, (SQLPOINTER)pcbValue,
SQL_IS_POINTER);// Set pcbValue/octet length
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

rc = SQLSetDescField(hdesc, icol, SQL_DESC_INDICATOR_PTR, (SQLPOINTER)pcbValue,
SQL_IS_POINTER);// Set pcbValue/indicator
if (!ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc,szSQLSETDESCFIELD))
return rc;

rc = SQLSetDescField(hdesc, icol, SQL_DESC_DATA_PTR, (SQLPOINTER)rgbValue,
SQL_IS_POINTER);// Set rgbValue
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc,szSQLSETDESCFIELD);

return rc;
}

//-----------------------------------------------------------------------
// Function: TestGetDescField
//-----------------------------------------------------------------------

void PASCAL TestGetDescField(QTSTRUCT * lpqt)
{
SQLRETURN rc=SQL_SUCCESS;
SQLUSMALLINT iDescType;

SQLHDESC hdesc1=NULL, hdesc2=NULL;
PTR rgbValue=NULL;
SQLSMALLINT swDescRecCount, swExpRecCount;
SQLINTEGER cbValue;
TCHARszName[MAX_DESC_BUF]=TEXT("");
TCHARszQuery[XLARGEBUFF]=TEXT("");

if (!Supported(SQL_API_SQLGETDESCFIELD))
{
// Allocate a default descriptor
rc = SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc);

if (RC_SUCCESSFUL(rc))
{
rc = SQLGetDescField(hdesc, 0, SQL_DESC_COUNT, &DescBuf, sizeof(DescBuf),
&cbValue);

if(!FindError(SQL_HANDLE_DESC,szIM001))
DISPLAYERROR(szSQLGETDESCFIELD, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLGETDESCFIELD);
}
}
else
{
// Make sure all parameters and columns are unbound
ResetHstmt(&hstmt);

// Perform a select stmt
wsprintf(szQuery,szSELECTSTAR,lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

// For each descriptor type
for (iDescType=0; iDescType < sizeof(DescTypes)/sizeof(DescTypes[0]); iDescType++)
{

// Get the handle for the automatically allocated descriptor
rc = SQLGetStmtAttr(hstmt, DescTypes[iDescType].uwDescType, &hdesc1, sizeof(hdesc1), &cbValue);
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR);

// Create a descriptor record so we have something to read besides header
swExpRecCount=CreateDescRecord(hdesc1, hstmt, DescTypes[iDescType].uwDescType);

// Get the count of descriptor records (iRecord ignored for header fields)
rc = SQLGetDescField(hdesc1, 0, SQL_DESC_COUNT, &swDescRecCount,
sizeof(swDescRecCount), &cbValue);

if (SQL_SUCCEEDED(rc))
{
// Check count value for expected
if (swExpRecCount != swDescRecCount)
{
wsprintf(buf,TEXT("Expected record count %d, found %d"),
swExpRecCount, swDescRecCount);
DISPLAYERROR(szSQLGETDESCFIELD, buf);
}

CheckDescriptor(hdesc1, DESC_UPDATE_MODE_READ, hstmt, iDescType);
}
else
{
// Unable to read record count from descriptor header, can't proceed
wsprintf(buf,TEXT("Unable to read record count from descriptor header for descriptor type %d"),
DescTypes[iDescType].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc1, SQL_SUCCESS, rc, buf);
}
}

rc=SQLCloseCursor(hstmt);
RETCHECK(SQL_SUCCESS, rc,szSQLCLOSECURSOR);
}


} // TestGetDescField()

//-----------------------------------------------------------------------
// Function: TestSetDescField
//-----------------------------------------------------------------------

void PASCAL TestSetDescField(QTSTRUCT * lpqt)
{
SQLRETURN rc=SQL_SUCCESS;
SQLUSMALLINT iDescType;

SQLHDESC hdesc1=NULL, hdesc2=NULL;
PTR rgbValue=NULL;
SQLSMALLINT swDescRecCount, swExpRecCount;
SQLINTEGER cbValue;
TCHARszName[MAX_DESC_BUF]=TEXT("");
TCHARszQuery[XLARGEBUFF]=TEXT("");


if (!Supported(SQL_API_SQLSETDESCFIELD))
{
// Allocate a default descriptor
rc = SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc);

if (RC_SUCCESSFUL(rc))
{
rc = SQLSetDescField(hdesc, 0, SQL_DESC_COUNT, &swDescRecCount,
sizeof(swDescRecCount));

if(!FindError(SQL_HANDLE_DESC,szIM001))
DISPLAYERROR(szSQLSETDESCFIELD, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLSETDESCFIELD);
}
}
else
{
// Make sure all parameters and columns are unbound
ResetHstmt(&hstmt);

// Perform a select stmt
wsprintf(szQuery,szSELECTSTAR,lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

// For each descriptor type
for (iDescType=0; iDescType < sizeof(DescTypes)/sizeof(DescTypes[0]); iDescType++)
{

// Get the handle for the automatically allocated descriptor
rc = SQLGetStmtAttr(hstmt, DescTypes[iDescType].uwDescType, &hdesc1, sizeof(hdesc1), &cbValue);
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR);

// Create a descriptor record so we have something to read besides header
swExpRecCount=CreateDescRecord(hdesc1, hstmt, DescTypes[iDescType].uwDescType);

// Update all descriptor records to new values
CheckDescriptor(hdesc1, DESC_UPDATE_MODE_WRITE, hstmt, iDescType);

// Compare the descriptor with the update values
// CompareDescriptor(hdesc1, NULL, iDescType, szSQLSETDESCFIELD);
}

ResetHstmt(&hstmt);
}

} //TestSetDescField()

//-----------------------------------------------------------------------
// Function: TestGetDescRec
//-----------------------------------------------------------------------

void PASCAL TestGetDescRec(QTSTRUCT * lpqt)
{
SQLRETURN rc=SQL_SUCCESS;
SQLUSMALLINT iDescType;

SQLHDESC hdesc1=NULL, hdesc2=NULL;
PTR rgbValue=NULL;
SQLSMALLINT swDescRec, swDescRecCount, iRecord, cbNameMax, cbName, fType, fSubType,
Precision, Scale, Nullable, swExpRecCount;
SQLINTEGER Length, cbValue;
TCHARszName[MAX_DESC_BUF]=TEXT("");
TCHARszQuery[XLARGEBUFF]=TEXT("");


if (!Supported(SQL_API_SQLGETDESCREC))
{
// Allocate a default descriptor
rc = SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc);

if (RC_SUCCESSFUL(rc))
{
rc = SQLGetDescRec(hdesc, iRecord, szName, cbNameMax, &cbName, &fType,
&fSubType, &Length, &Precision, &Scale, &Nullable);

if(!FindError(SQL_HANDLE_DESC,szIM001))
DISPLAYERROR(szSQLGETDESCREC, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLGETDESCREC);
}
}
else
{
// Make sure all parameters and columns are unbound
ResetHstmt(&hstmt);

// Turn on bookmarks so we can retrieve from record 0 of ARD
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_USE_BOOKMARKS, (SQLPOINTER)SQL_UB_ON, SQL_IS_INTEGER);
RETCHECK(SQL_SUCCESS, rc,szSQLSETSTMTATTR);

// Perform a select stmt
wsprintf(szQuery,szSELECTSTAR,lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

// For each descriptor type
for (iDescType=0; iDescType < sizeof(DescTypes)/sizeof(DescTypes[0]); iDescType++)
{

// Get the handle for the automatically allocated descriptor
rc = SQLGetStmtAttr(hstmt, DescTypes[iDescType].uwDescType, &hdesc1, sizeof(hdesc1), &cbValue);
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR);

// Create a descriptor record so we have something to read besides header
swExpRecCount=CreateDescRecord(hdesc1, hstmt, DescTypes[iDescType].uwDescType);

// Get the count of descriptor records
// Count for automatically allocated descriptors is written in
// SetDescField test.
rc = SQLGetDescField(hdesc1, 0, SQL_DESC_COUNT, &swDescRecCount,
sizeof(swDescRecCount), &cbValue);

if (SQL_SUCCESS == rc)
{

// For all descriptor records
for (swDescRec=0; swDescRec <= swDescRecCount+1; swDescRec++)
{
// Read the record
rc = SQLGetDescRec(hdesc1, swDescRec, szName, sizeof(szName), &cbName, &fType,
&fSubType, &Length, &Precision, &Scale, &Nullable);

if (swDescRec <= swDescRecCount)
{
// Retrieving bookmark on IPD is an error
if (SQL_ATTR_IMP_PARAM_DESC == DescTypes[iDescType].uwDescType && 0 == swDescRec)
{
wsprintf(buf,TEXT("%s: Able to read record %d from %s"),
szSQLGETDESCREC, swDescRec, DescTypes[iDescType].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc1, SQL_ERROR, rc, buf);
}
else
{
wsprintf(buf,TEXT("%s: Unable to read record %d from %s"),
szSQLGETDESCREC, swDescRec, DescTypes[iDescType].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc1, SQL_SUCCESS, rc, buf);
}
}
else
{
wsprintf(buf,TEXT("%s: Did not receive SQL_NO_DATA reading record %d from %s"),
szSQLGETDESCREC, swDescRec, DescTypes[iDescType].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc1, SQL_NO_DATA, rc, buf);
}
}
}
else
{
// Unable to read record count from descriptor header, can't proceed
wsprintf(buf,TEXT("\t\tUnable to read record count from descriptor header for %s"),
DescTypes[iDescType].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc1, SQL_SUCCESS, rc, buf);
}
}

rc=SQLCloseCursor(hstmt);
RETCHECK(SQL_SUCCESS, rc,szSQLCLOSECURSOR);

}

} //TestGetDescRec()

//-----------------------------------------------------------------------
// Function: TestSetDescRec
//-----------------------------------------------------------------------

void PASCAL TestSetDescRec(QTSTRUCT * lpqt)
{
SQLRETURN rc=SQL_SUCCESS;
SQLINTEGER cbLength, cbValue, Indicator;
SQLUSMALLINT iDescType;

SQLHDESC hdesc1=NULL, hdesc2=NULL;
PTR rgbValue=NULL;
SQLSMALLINT swDescRec, swDescRecCount, swStartRec=1, iRecord, fType, fSubType,
ibPrecision, ibScale;
SQLSMALLINT swExpRecCount;
TCHARszQuery[XLARGEBUFF]=TEXT("");


if (!Supported(SQL_API_SQLSETDESCREC))
{
// Allocate a default descriptor
rc = SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc);

if (RC_SUCCESSFUL(rc))
{
rc = SQLSetDescRec(hdesc, iRecord, fType, fSubType, cbLength,
ibPrecision, ibScale, &DescBuf, &cbValue, &Indicator);

if(!FindError(SQL_HANDLE_DESC,szIM001))
DISPLAYERROR(szSQLSETDESCREC, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLSETDESCREC);
}
}
else
{
// Make sure all parameters and columns are unbound
ResetHstmt(&hstmt);

// Perform a select stmt
wsprintf(szQuery,szSELECTSTAR,lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

// If bookmarks are supported, then we want to try to set record 0
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_USE_BOOKMARKS,
(PTR)SQL_UB_VARIABLE, SQL_IS_INTEGER);
if (RC_SUCCESSFUL(rc))
swStartRec=0;

// For each descriptor type
for (iDescType=0; iDescType < sizeof(DescTypes)/sizeof(DescTypes[0]); iDescType++)
{

// Get the handle for the automatically allocated descriptor
rc = SQLGetStmtAttr(hstmt, DescTypes[iDescType].uwDescType, &hdesc1, sizeof(hdesc1), &cbValue);
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR);

// Create a descriptor record so we have something to read besides header
swExpRecCount=CreateDescRecord(hdesc1, hstmt, DescTypes[iDescType].uwDescType);

// Get the count of descriptor records
rc = SQLGetDescField(hdesc1, 0, SQL_DESC_COUNT, &swDescRecCount,
sizeof(swDescRecCount), &cbValue);

if (SQL_SUCCESS == rc)
{

// For all descriptor records
for (swDescRec=swStartRec; swDescRec <= swDescRecCount; swDescRec++)
{
// Test with a DATETIME record so we can check the subtype field.
SQLINTEGER Indicator=0;
fType=SQL_DATETIME;
fSubType=SQL_CODE_TIMESTAMP;
cbLength=sizeof(TIMESTAMP_STRUCT);
ibPrecision=5; // For DATETIME, SQL_CODE_DATE data
// precision defaults to 0, but it implies it can be set otherwise.
ibScale=0;// Only used for DECIMAL or NUMERIC types, otherwise undefined

CheckDescRecord(hdesc1, swDescRec, fType, fSubType, cbLength, ibPrecision,
ibScale, &tsval, &cbValue, &Indicator, iDescType);

// Test with a numeric record to test scale
Indicator=0;
fType=SQL_NUMERIC;
fSubType=SQL_CODE_TIMESTAMP;
cbLength=sizeof(TIMESTAMP_STRUCT);
ibPrecision=5; // For DATETIME, SQL_CODE_DATE data
// precision defaults to 0, but it implies it can be set otherwise.
ibScale=0;// Only used for DECIMAL or NUMERIC types, otherwise undefined

CheckDescRecord(hdesc1, swDescRec, fType, fSubType, cbLength, ibPrecision,
ibScale, &tsval, &cbValue, &Indicator, iDescType);
}
}
else
{
// Unable to read record count from descriptor header,
// can't proceed
wsprintf(buf,TEXT("\t\tUnable to read record count from descriptor header for %s"),
DescTypes[iDescType].szDescName);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc1, SQL_SUCCESS, rc, buf);
}
}
ResetHstmt(&hstmt);
}

} //TestSetDescRec()

//-----------------------------------------------------------------------
// Function: TestCopyDesc
//-----------------------------------------------------------------------

void PASCAL TestCopyDesc(QTSTRUCT * lpqt)
{
SQLRETURN rc=SQL_SUCCESS;
SQLUSMALLINT iDescType;
SQLSMALLINT swExpRecCount;

SQLHDESC hdesc1=NULL, hdesc2=NULL;
PTR rgbValue=NULL;
TCHARszName[MAX_DESC_BUF]=TEXT("");
TCHARszQuery[XLARGEBUFF]=TEXT("");


// Allocate a default descriptor
rc = SQLAllocHandle(SQL_HANDLE_DESC, hdbc, &hdesc);

if (RC_SUCCESSFUL(rc))
{

if (!Supported(SQL_API_SQLCOPYDESC))
{
rc = SQLCopyDesc(hdesc, hdesc1);

if(!FindError(SQL_HANDLE_DESC,szIM001))
DISPLAYERROR(szSQLCOPYDESC, szNotSupported);
RETCHECK(SQL_ERROR, rc,szSQLCOPYDESC);
}
else
{
ResetHstmt(&hstmt);

// Perform a select stmt
wsprintf(szQuery,szSELECTSTAR,lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

// For each descriptor type
for (iDescType=0; iDescType < sizeof(DescTypes)/sizeof(DescTypes[0]); iDescType++)
{
// Get the handle for the automatically allocated descriptor
rc = SQLGetStmtAttr(hstmt, DescTypes[iDescType].uwDescType, &hdesc1, sizeof(hdesc1), &cbValue);
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR);

// Create a descriptor record so we have something to read besides header
swExpRecCount=CreateDescRecord(hdesc1, hstmt, DescTypes[iDescType].uwDescType);

// Update all descriptor records to new values
CheckDescriptor(hdesc1, DESC_UPDATE_MODE_WRITE, hstmt, iDescType);

// Copy the descriptor to the newly allocated descriptor
rc = SQLCopyDesc(hdesc1, hdesc);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc1, SQL_SUCCESS, rc, szSQLCOPYDESC);
ERRSHANDLE(SQL_HANDLE_DESC, hdesc, SQL_SUCCESS, rc, szSQLCOPYDESC);

// Compare each field in the copied descriptor to the original
if (RC_SUCCESSFUL(rc))
CompareDescriptor(hdesc1, hdesc, iDescType, szSQLCOPYDESC);
}

ResetHstmt(&hstmt);
}

// Free descriptor handle
if (hdesc)
{
rc = SQLFreeHandle(SQL_HANDLE_DESC, hdesc);
RETCHECK(SQL_SUCCESS, rc,szSQLFREEHANDLE);
}
}

} //TestCopyDesc()


//-----------------------------------------------------------------------
// Function: TestDescDefaults
//-----------------------------------------------------------------------
void PASCAL TestDescDefaults(QTSTRUCT * lpqt)
{
SQLRETURN rc=SQL_SUCCESS;
SQLINTEGER cbValue;
SQLSMALLINT swDescRec;
SQLUSMALLINT iDescField, iStartField=0, iDescType;

if (Supported(SQL_API_SQLGETDESCFIELD))
{

// Reallocate hstmt to check defaults against
ResetHstmt(&hstmt);

// For each descriptor type
for (iDescType=0; iDescType < sizeof(DescTypes)/sizeof(DescTypes[0]); iDescType++)
{
// Get the handle for the descriptor
rc = SQLGetStmtAttr(hstmt, DescTypes[iDescType].uwDescType, &hdesc, sizeof(hdesc), NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR);

swDescRec=0;

// For each descriptor field, read the value
for (iDescField=iStartField; iDescField < sizeof(rgDescInfo)/sizeof(rgDescInfo[0]); iDescField++) {

// If we've gone past the header fields, then create a record and check record fields
if (0 == swDescRec && !rgDescInfo[iDescField].fHeader) {
iStartField=iDescField;
// Make sure we're not on the IRD, 'cause you can't modify an IRD
if (SQL_ATTR_IMP_ROW_DESC != DescTypes[iDescType].uwDescType)
{
// Create a record with all default values
rc = SQLSetDescField(hdesc, 0, SQL_DESC_COUNT, (SQLPOINTER)1,
SQL_IS_SMALLINT);
RETCHECK(SQL_SUCCESS, rc,szSQLSETDESCFIELD);

swDescRec++;
}
continue;
}

// If the field has a default value
if (rgDescInfo[iDescField].fDefault & DescTypes[iDescType].uwTypeMask)
{
// Read from the field
rc = SQLGetDescField(hdesc, swDescRec, rgDescInfo[iDescField].uwDescField,
&DescBuf, sizeof(DescBuf), &cbValue);
RETCHECK(SQL_SUCCESS, rc,szSQLGETDESCFIELD);

if (RC_SUCCESSFUL(rc))
{
// Perform the compare
if (memcmp(DescBuf, &rgDescInfo[iDescField].DefaultVal, cbValue))
{
// Compare error, print message
wsprintf(buf,TEXT("Error in %s field %s default value"),
DescTypes[iDescType].szDescName,rgDescInfo[iDescField].szDescFieldName);
DISPLAYERROR(szSQLGETDESCFIELD, buf);
}
}
}
}
}
}
} // TestDescDefaults

//-----------------------------------------------------------------------
// Function: TestUseDesc
//-----------------------------------------------------------------------
void PASCAL TestUseDesc(QTSTRUCT * lpqt, FIELDINFO *rgFields)
{
LPTSTR pch=NULL;
SDWORD crow=0;
TCHARszQuery[XLARGEBUFF]=TEXT("");
SQLRETURN rc;
UWORDrow, icol, paramno;
SWORDccol;
TCHAR * rgbMemBuf=NULL, * rgbValue=NULL;
SDWORD * pcbValueBuf=NULL, * pcbValue=NULL;

if (Supported(SQL_API_SQLSETDESCFIELD))
{

// Test descriptor bindcol

// Reallocate hstmt to set to defaults
ResetHstmt(&hstmt);

// Perform a select stmt
wsprintf(szQuery,szSELECTSTAR,lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

rc=SQLNumResultCols(hstmt, &ccol);
RETCHECK(SQL_SUCCESS, rc, szSQLNUMRESULTCOLS);

rc=SQLFreeStmt(hstmt, SQL_CLOSE); // Close the cursor so we can insert
RETCHECK(SQL_SUCCESS, rc,szSQLFREESTMT);

rgbMemBuf=AllocateMemory(ccol*MAX_STRING_SIZE*sizeof(TCHAR));

if (!rgbMemBuf)
{
DISPLAYERROR(TEXT("Simulate BindCol"),TEXT("Out of memory!"));
goto ExitUseDesc;
}

pcbValueBuf=(SDWORD *)AllocateMemory(ccol*sizeof(SDWORD));

if (!pcbValueBuf)
{
DISPLAYERROR(TEXT("Simulate BindCol"),TEXT("Out of memory!"));
goto ExitUseDesc;
}

// Bind a parameter for each column in the result set using descriptors
paramno=0;
for (icol=1, rgbValue=rgbMemBuf, pcbValue=pcbValueBuf;
icol <= ccol;
icol++, rgbValue+=MAX_STRING_SIZE*sizeof(TCHAR), pcbValue++)
{
pch = qtMakeData(guwRowCount, icol-1, &rgFields[icol-1], rgbValue);

// Field is not READ ONLY
if(pch)
{
// Where row and column match and field is nullable, make NULL
if (icol == guwRowCount && rgFields[icol-1].nullable)
*pcbValue=SQL_NULL_DATA;
else
*pcbValue=SQL_NTS;

paramno++;
//rc=SQLBindParameter(hstmt, paramno, SQL_PARAM_INPUT, SQL_C_CHAR,
rc=DescBindParameter(hstmt, paramno, SQL_PARAM_INPUT, SQL_C_CHAR,
rgFields[icol-1].wSQLType, rgFields[icol-1].precision,
rgFields[icol-1].scale, rgbValue, 0, pcbValue);
RETCHECK(SQL_SUCCESS, rc,TEXT("DescBindParameter"));
}
}

// Insert the new row. Note that the inserted row will be checked
// for accuracy below.
rc = SQLExecDirect(hstmt, szInsertStmt, SQL_NTS);
ERRSHANDLE(SQL_HANDLE_STMT, hstmt, SQL_SUCCESS, rc, szSQLEXECDIRECT);

if (RC_SUCCESSFUL(rc))
guwRowCount++;

// Perform the select stmt again
wsprintf(szQuery,szSELECTSTAR,lpqt->szTableName);
rc = SQLExecDirect(hstmt, szQuery, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

// Bind all the columns to char
for (icol=1, rgbValue=rgbMemBuf, pcbValue=pcbValueBuf;
icol <= ccol;
icol++, rgbValue+=MAX_STRING_SIZE*sizeof(TCHAR), pcbValue++)
{
// Bind to next column
rc = DescBindCol(hstmt, icol, SQL_C_CHAR, rgbValue, MAX_STRING_SIZE*sizeof(TCHAR), pcbValue);
//rc = SQLBindCol(hstmt, icol, SQL_C_CHAR, rgbValue, MAX_STRING_SIZE*sizeof(TCHAR), pcbValue);
RETCHECK(SQL_SUCCESS, rc, TEXT("DescBindCol"));
}

// Fetch all the rows and check the bound value with MakeAData for
// each column
for (row=1; rc == SQL_SUCCESS; row++)
{
rc=SQLFetch(hstmt); // Will return SQL_NO_DATA after last row

if (SQL_SUCCESS == rc)
{

// Compare all the columns
for (icol=1, rgbValue=rgbMemBuf, pcbValue=pcbValueBuf;
icol <= ccol;
icol++, rgbValue+=MAX_STRING_SIZE*sizeof(TCHAR), pcbValue++)
{

CompareWithExpected(rgbValue, pcbValue, row, icol, rgFields);
}
}
else
ERRSHANDLE(SQL_HANDLE_STMT, hstmt, SQL_NO_DATA, rc, szSQLFETCH);

}

}

ExitUseDesc:
rc=SQLFreeStmt(hstmt, SQL_CLOSE);
RETCHECK(SQL_SUCCESS, rc,szSQLFREESTMT);

if (rgbMemBuf)
ReleaseMemory(rgbMemBuf);
if (pcbValueBuf)
ReleaseMemory(pcbValueBuf);


} // TestUseDesc

//-----------------------------------------------------------------------
// Function: TestEnvAttr
//
// Note:Only one environment attribute (SQL_ATTR_OUTPUT_NTS) exists at this
// time. The default value is SQL_TRUE.
//
//For the Environment attributes we assume support if DM version >= 3.00
//-----------------------------------------------------------------------

void PASCAL TestEnvAttr()
{
SQLINTEGERsdwEnvAttr;
SQLRETURNrc;
HENVtshenv;
BOOLfSetSupported = Supported(SQL_API_SQLSETENVATTR),
fGetSupported = Supported(SQL_API_SQLGETENVATTR);


//allocate an enviroment handle
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &tshenv);
RETCHECK(SQL_SUCCESS, rc,szSQLALLOCHANDLE);


//-------------------------------
//-------- SQLSetEnvAttr --------
//-------------------------------
// Set to SQL_FALSE
rc = SQLSetEnvAttr(tshenv, SQL_ATTR_ODBC_VERSION, (PTR)SQL_OV_ODBC3,
SQL_IS_UINTEGER);

if(!fSetSupported)
{
if (!FindError(SQL_HANDLE_ENV,szIM001))
DISPLAYERROR(szSQLSETENVATTR, szNotSupported);

RETCHECK(SQL_ERROR, rc,szSQLSETENVATTR);
}
else
RETCHECK(SQL_SUCCESS, rc,szSQLSETENVATTR);

if (RC_SUCCESSFUL(rc))
{
// Read it again
rc = SQLGetEnvAttr(tshenv, SQL_ATTR_ODBC_VERSION, &sdwEnvAttr,
sizeof(sdwEnvAttr), NULL);

if(!fGetSupported)
{
if (!FindError(SQL_HANDLE_ENV,szIM001))
DISPLAYERROR(szSQLGETENVATTR, szNotSupported);

RETCHECK(SQL_ERROR, rc,szSQLGETENVATTR);
}
else
RETCHECK(SQL_SUCCESS, rc,szSQLGETENVATTR);

if (RC_SUCCESSFUL(rc) && (SQL_OV_ODBC3 != sdwEnvAttr))
{
// New value wrong
DISPLAYERROR(szSQLSETENVATTR,TEXT("Unable to set environment attribute SQL_ATTR_ODBC_VERSION to SQL_OV_ODBC3."));
}
}

rc = SQLFreeHandle(SQL_HANDLE_ENV,tshenv);
RETCHECK(SQL_SUCCESS, rc,szSQLFREEHANDLE);

//-------------------------------
//-------- SQLGetEnvAttr --------
//-------------------------------
rc = SQLAllocEnv(&tshenv);
RETCHECK(SQL_SUCCESS, rc,szSQLALLOCENV);

rc = SQLGetEnvAttr(tshenv, SQL_ATTR_ODBC_VERSION, &sdwEnvAttr,
sizeof(sdwEnvAttr), NULL);

if(!fGetSupported)
{
if (!FindError(SQL_HANDLE_ENV,szIM001))
DISPLAYERROR(szSQLGETENVATTR, szNotSupported);

RETCHECK(SQL_ERROR, rc,szSQLGETENVATTR);
}
else
RETCHECK(SQL_SUCCESS, rc,szSQLGETENVATTR);


if (RC_SUCCESSFUL(rc) && (SQL_OV_ODBC2 != sdwEnvAttr))
{
// Default value wrong
DISPLAYERROR(szSQLGETENVATTR,TEXT("Value was not SQL_OV_ODBC3!"));
}


rc = SQLFreeHandle(SQL_HANDLE_ENV,tshenv);
RETCHECK(SQL_SUCCESS, rc,szSQLFREEHANDLE);

} //TestEnvAttr()


//-----------------------------------------------------------------------
// Function: GetRowCnt
//
//Get the row count of the table lpqt->szTalbeName
//-----------------------------------------------------------------------
SWORD PASCAL GetRowCnt(QTSTRUCT *lpqt)
{
SWORD sRowCnt;
RETCODErc;

// free the statment handle for a fresh start
FreeStmt(SQL_DROP);

AllocHstmt();

//get row count for the table
wsprintf(lpqt->sz,TEXT("select count(*) from %s"), lpqt->szTableName);

rc = SQLExecDirect(hstmt, lpqt->sz, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECDIRECT);

rc = SQLFetch(hstmt);
RETCHECK(SQL_SUCCESS, rc,szSQLFETCH);

rc = SQLGetData(hstmt,1, SQL_C_SSHORT, &sRowCnt, sizeof(sRowCnt), NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETDATA);

FreeStmt(SQL_CLOSE);

return sRowCnt;
}//GetRowCount


//-----------------------------------------------------------------------
// Function: InsertOneRow
//
//Insert one row into the table.
//
//lpqt:temp place to hold data genrated
//rgFields:hold info about each data type in the dbs
//cTypes;the count of data types
//lpd:buffer used for SQLBindParam
//cSeed;the seed used to generate data
//fFreeHsmt:if the hstmt should be freed
//
//Return TRUE: the insertion was successful
// FALSE: otherwise
//-----------------------------------------------------------------------


BOOL PASCAL InsertOneRow(QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes,
DSTRUCT *lpd,UWORD cSeed,BOOL fFreeHstmt)
{
RETCODE rc;
UWORD w;
UWORD ind;
LPTSTR pch=NULL;
SQLHDESChdesc;
SQLSMALLINT fParamType = SQL_PARAM_INPUT;
TCHARszDataItem[150];


//prepare the insert statement
rc = SQLPrepare(hstmt, szInsertStmt, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPREPARE);

//going through each data type, call SQLBindParam
for(ind = 0, w = 0; ind < cTypes; ind++)
{

//make data according to data type description in rgFields[], cSeed is the seed
pch = qtMakeData(cSeed,ind,&rgFields[ind], szDataItem);

//if the data type is READ_ONLY, skip
if(!pch)
continue;

//populate the NULL data
if(*pch)
{
lstrcpy(lpd[w].data, pch);
lpd[w].cb = SQL_NTS;
}
else
{
lstrcpy(lpd[w].data,TEXT(""));
lpd[w].cb = SQL_NULL_DATA;
}

if (g_f3XDriver && Supported(SQL_API_SQLSETDESCFIELD))
//if (0)//DEBUG
{
//bind the data buffer in lpd[] to each column of the table
rc = SQLBindParam(hstmt, (SQLSMALLINT)(w+1),SQL_C_TCHAR, rgFields[ind].wSQLType,
rgFields[ind].precision, rgFields[ind].scale,
&lpd[w].data, &lpd[w].cb);
RETCHECK(SQL_SUCCESS, rc,szSQLBINDPARAM);

if(rc!= SQL_SUCCESS)
return FALSE;

//get the driver parameter descriptor handle
rc = SQLGetStmtAttr(hstmt,SQL_ATTR_IMP_PARAM_DESC, &hdesc, sizeof(hdesc),NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETSTMTATTR);
if(rc!= SQL_SUCCESS)
return FALSE;

//set the parameter type field in the descriptor
rc = SQLSetDescField(hdesc, (SQLSMALLINT)(w+1), SQL_DESC_PARAMETER_TYPE,
(PTR)fParamType, sizeof(fParamType));
RETCHECK(SQL_SUCCESS, rc,szSQLSETDESCFIELD);

}
else
{
rc = SQLBindParameter(hstmt, (UWORD)(w+1),SQL_PARAM_INPUT,SQL_C_TCHAR,
rgFields[ind].wSQLType,rgFields[ind].precision,
rgFields[ind].scale,&lpd[w].data, 100, &lpd[w].cb);

RETCHECK(SQL_SUCCESS, rc,szSQLBINDPARAMETER);

if(rc!= SQL_SUCCESS)
return FALSE;
}


//go on to next column
w++;
}

// insert a row
rc = SQLExecute(hstmt);
RETCHECK(SQL_SUCCESS, rc,szSQLEXECUTE);
if(rc!= SQL_SUCCESS)
return FALSE;

//free the hstmt if required
if(fFreeHstmt)
{
if (FreeStmt(SQL_CLOSE))
return FALSE;

}

return TRUE;

}//InsertOneRow

//-----------------------------------------------------------------------
// Function: ExecEndTran
//
//Execute EndTran statement, return the row count of the table
//-----------------------------------------------------------------------
SWORD PASCAL ExecEndTran(QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes,
DSTRUCT *lpd,SQLSMALLINT fHandleType, SQLSMALLINT fMode,
UWORD cSeed)
{
RETCODE rc;

//does not free the hstmt
if(!InsertOneRow(lpqt,rgFields,cTypes,lpd,cSeed, FALSE))
return (INSERT_FAIL);

switch(fHandleType)
{
case SQL_HANDLE_ENV:
rc= SQLEndTran(fHandleType,henv,fMode);
break;
case SQL_HANDLE_DBC:
rc= SQLEndTran(fHandleType,hdbc,fMode);
break;
default:
DISPLAYERROR(TEXT("ExecEndTran"),TEXT("Did not recieve correct handle type"));
break;
}
RETCHECK(SQL_SUCCESS, rc,szSQLENDTRAN);

if (SQL_COMMIT == fMode && SQL_SUCCESS == rc)
guwRowCount++;// We inserted a row, increment global row count

//free the hstmt handle
FreeStmt(SQL_CLOSE);

return(GetRowCnt(lpqt));
} //ExecEndTran

//-----------------------------------------------------------------------
// Function: TestEndTran
//
//Test the SQLEndTran for SDK 3.0
//-----------------------------------------------------------------------
void PASCAL TestEndTran(QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes,
DSTRUCT *lpd)
{
SWORDi;
RETCODE rc;
SWORDsTXN;
SWORDsRowCnt_Pre;
SWORDsRowCnt_Aft;
SQLSMALLINT fHandleType[]={SQL_HANDLE_ENV, SQL_HANDLE_DBC};
SDWORDcbValue=0;

//check if transaction is supported
rc = SQLGetInfo(hdbc, SQL_TXN_CAPABLE, &sTXN, sizeof(sTXN), NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

//check both henv and hdbc handle
for(i=0; i<=1; i++)
{

//get the row count of the table
sRowCnt_Pre = GetRowCnt(lpqt);

//check if SQLEndTran is supported
if(Supported(SQL_API_SQLENDTRAN))
{
if(sTXN != SQL_TC_NONE)
{
//set the autocommit to off
SetConnectionAttributes(hdbc,SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);

//insert one row, and call SQLEndTran to roll back the transction
sRowCnt_Aft = ExecEndTran(lpqt,rgFields,cTypes,lpd,fHandleType[i],SQL_ROLLBACK,(UWORD)(guwRowCount));

//the row count should be the same
if(sRowCnt_Aft != sRowCnt_Pre)
DISPLAYERROR(szSQLENDTRAN,TEXT("Did not Roll Back the insert transaction"));
}

//insert one row, and call SQLEndTran to commit the transction
sRowCnt_Aft = ExecEndTran(lpqt,rgFields,cTypes,lpd,fHandleType[i],SQL_COMMIT,(UWORD)(guwRowCount));

//the row count should be increased by 1
if(sRowCnt_Aft != (sRowCnt_Pre+1) )
DISPLAYERROR(szSQLENDTRAN,TEXT("Did not commit the insert transaction"));

if( sTXN != SQL_TC_NONE)
{
//set the autocommit to off
SetConnectionAttributes(hdbc,SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF);
}

}//end of the situation where SQLEndTran is supported
else
{

switch(fHandleType[i])
{
case SQL_HANDLE_ENV:
rc= SQLEndTran(fHandleType[i],henv,SQL_COMMIT);
break;
case SQL_HANDLE_DBC:
rc= SQLEndTran(fHandleType[i],hdbc,SQL_ROLLBACK);
break;
}

//check error msg
if(!FindError(fHandleType[i],szIM001))
DISPLAYERROR(szSQLENDTRAN,TEXT("Did not return not Supported Message"));

RETCHECK(SQL_ERROR, rc,szSQLENDTRAN);

//free the hstmt handle
FreeStmt(SQL_CLOSE);

}//end of error msg checking

}//end of the loop to check both henv and hdbc

// Commit the transaction started in GetRowCnt inside ExecEndTran
rc= SQLEndTran(SQL_HANDLE_ENV,henv,SQL_COMMIT);
RETCHECK(SQL_SUCCESS, rc,szSQLENDTRAN);

SetConnectionAttributes(hdbc,SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_ON);

//free the hstmt handle
FreeStmt(SQL_CLOSE);

} // TestEndTran()


//-----------------------------------------------------------------------
// Function: TestBindParam
//
//Test the SQLBindParam for SDK 3.0
//-----------------------------------------------------------------------
void PASCAL TestBindParam(QTSTRUCT *lpqt,FIELDINFO *rgFields,UWORD cTypes,
DSTRUCT *lpd)
{
RETCODE rc;
SQLINTEGERcbValue = SQL_NTS;
TCHARszDataItem[150];

if(Supported(SQL_API_SQLBINDPARAM))
{
//test SQLBindParam by inserting one row, do not free the hstmt handle
if (InsertOneRow(lpqt,rgFields,cTypes,lpd,(UWORD)(guwRowCount),FALSE))
guwRowCount++; // We've inserted a row
}
else
{
//prepare the insert statement
rc = SQLPrepare(hstmt, szInsertStmt, SQL_NTS);
RETCHECK(SQL_SUCCESS, rc,szSQLPREPARE);

//make data according to data type description in rgFields[0], cSeed is the seed
qtMakeData(cTypes+1,0,&rgFields[0],szDataItem);

//try to bind lpqt->szDataItem to the first column of the table
rc = SQLBindParam(hstmt,1,SQL_C_TCHAR, rgFields[0].wSQLType, rgFields[0].precision,
rgFields[0].scale,lpqt->szDataItem, &cbValue);

//check error msg
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLBINDPARAM,TEXT("Did not return Not Supported Message"));

RETCHECK(SQL_ERROR, rc,szSQLBINDPARAM);
}

//free the hstmt handle
FreeStmt(SQL_CLOSE);

} // TestBindParam()






//-----------------------------------------------------------------------
// Function: TestFetchScroll
//
//Test the SQLFetchScroll for SDK 3.0
//-----------------------------------------------------------------------

void PASCAL TestFetchScroll(QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
TCHARszSave[MAX_QUERY_SIZE];
DWORDdwLen=0;
SDWORDcbValue=0;

/* check if SQLGetFunctions says your API is supported */
if(!Supported( SQL_API_SQLFETCHSCROLL))
{
/* Call your 3.0 API here and verify that the correct state is returned */
rc = SQLFetchScroll(hstmt,SQL_FETCH_FIRST, 0);
if(!FindError(SQL_HANDLE_STMT,szIM001))
DISPLAYERROR(szSQLFETCHSCROLL, szNotSupported);
RETCHECK(SQL_ERROR, rc, szSQLFETCHSCROLL);
}
else
{
ResetHstmt(&hstmt);

/* Valid Testing of SQLFetchScroll */
SelectFromTable(lpqt);

rc = SQLBindCol(hstmt, 1, SQL_C_TCHAR, lpqt->sz, MAX_BIND_ARRAY_ELEMENT,
&cbValue);
RETCHECK(SQL_SUCCESS, rc, szSQLBINDCOL);

rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 3);
RETCHECK(SQL_SUCCESS, rc, szSQLFETCHSCROLL);
lstrcpy(szSave,lpqt->sz);

rc = SQLFetchScroll(hstmt, SQL_FETCH_PRIOR, 1);
RETCHECK(SQL_ERROR, rc, szSQLFETCHSCROLL);

rc = SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 0);
RETCHECK(SQL_ERROR, rc, szSQLFETCHSCROLL);

rc = SQLFetchScroll(hstmt, SQL_FETCH_LAST, 0);
RETCHECK(SQL_ERROR, rc, szSQLFETCHSCROLL);

rc = SQLFreeStmt(hstmt, SQL_CLOSE);
RETCHECK(SQL_SUCCESS, rc, szSQLFREESTMT);

}

//free the hstmt handle
FreeStmt(SQL_CLOSE);

} // TestFetchScroll()



/*-----------------------------------------------------------------------*/
/* Function: qtDisplayError */
/*-----------------------------------------------------------------------*/

void PASCAL qtDisplayError(LPTSTR szFunction, LPTSTR buf, LPTSTR szFile, int iLine)
{
TCHAR szTmp[MAX_STRING_SIZE];

szWrite(TEXT(""), TRUE);
szWrite(TEXT("\t\t\t"), FALSE);
szWrite(szFunction, FALSE);
szWrite(TEXT(" FAILED"), TRUE);
szWrite(TEXT("\t\t\t"), FALSE);
szWrite(buf, TRUE);

wsprintf(szTmp,TEXT("\t\t\t%s: %d"), szFile, iLine);
szWrite(szTmp, TRUE);

szWrite(TEXT("\t\t\t -------- "), TRUE);

lpSI->failed++;

return;
}


/*-----------------------------------------------------------------------*/
/* Function: qtMakeData */
/*-----------------------------------------------------------------------*/

LPTSTR PASCAL qtMakeData(int row, int col,FIELDINFO *rgField, LPTSTR buf)
{
if(rgField->fAutoUpdate)
return NULL;

if(rgField->nullable && (row==col))
{
lstrcpy(buf,TEXT(""));
return buf;
}

switch(rgField->wSQLType) {
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
if(rgField->precision < 4)
wsprintf(buf,TEXT("%d"), 1);
else
wsprintf(buf,TEXT("%d%d"), row, row);
break;
case SQL_DECIMAL:
case SQL_NUMERIC:
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
if(row == 2 && !rgField->fUnsigned) /* make the second row negative for variety */
wsprintf(buf,TEXT("-%d.%d"), row, row);
else
wsprintf(buf,TEXT("%d.%d"), row, row);
break;

case SQL_BIT:
if(row > 2)
wsprintf(buf,TEXT("%d"), 1);
else
wsprintf(buf,TEXT("%d"), 0);

break;

case SQL_SMALLINT:
case SQL_INTEGER:
case SQL_TINYINT:
case SQL_BIGINT:
if(row == 2 && !rgField->fUnsigned) /* make the second row negative for variety */
wsprintf(buf,TEXT("-%d"), row);
else
wsprintf(buf,TEXT("%d"), row);
break;

case SQL_TIME:
case SQL_TYPE_TIME:
wsprintf(buf,TEXT("{t '01:%02d:%02d'}"), row % 60, row % 60);
break;
case SQL_DATE:
case SQL_TYPE_DATE:
wsprintf(buf,TEXT("{d '1994-%02d-%02d'}"), (row % 12) + 1, (row % 30) + 1);
break;
case SQL_TIMESTAMP:
case SQL_TYPE_TIMESTAMP:
wsprintf(buf,TEXT("{ts '1994-%02d-%02d 01:%02d:%02d'}"), (row % 12) + 1, (row % 30) + 1, row % 60, row % 60);
break;
}
return buf;
}




/*-----------------------------------------------------------------------*/
/* Function: TestNumResCols */
/*-----------------------------------------------------------------------*/

VOID PASCAL TestNumResCols(SDWORD cColsSelected,QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
SWORDwNum;

SelectFromTable(lpqt);

rc = SQLNumResultCols(hstmt, &wNum);
RETCHECK(SQL_SUCCESS, rc,szSQLNUMRESULTCOLS);

if(wNum != cColsSelected)
DISPLAYERROR(szSQLNUMRESULTCOLS,TEXT("incorrect value returned"));

FreeStmt(SQL_CLOSE);

} /* TestNumResCols() */



/*-----------------------------------------------------------------------*/
/* Function: TestNumResCols */
/*-----------------------------------------------------------------------*/

VOID PASCAL TestCursorName(QTSTRUCT *lpqt)
{
RETCODErc=SQL_SUCCESS;
TCHARszCursorName[MEDBUFF];
SWORDcb=0;

SelectFromTable(lpqt);

/* the cursor name should be created by the driver since one was*/
/* not specified*/

rc = SQLGetCursorName(hstmt, szCursorName, MAX_STRING_SIZE, &cb);
RETCHECK(SQL_SUCCESS, rc,szSQLGETCURSORNAME);

if(cb > MAX_STRING_SIZE)
{
DISPLAYERROR(szSQLGETCURSORNAME,TEXT("invalid cb"));
cb = MAX_STRING_SIZE;
}

/* don't check the name itself, just make sure that*/
/*something was returned */
if(!*szCursorName)
DISPLAYERROR(szSQLGETCURSORNAME,TEXT("no name returned"));

FreeStmt(SQL_CLOSE);

} /* TestCursorName() */


//-----------------------------------------------------------------------
// Function: GetDMVersion
//-----------------------------------------------------------------------

UWORD PASCAL GetDMVersion()
{
RETCODErc=SQL_SUCCESS;
TCHARszNum[MAX_NUM_BUFFER];

rc = SQLGetInfo(hdbc, SQL_ODBC_VER, &szNum, MAX_NUM_BUFFER, NULL);
RETCHECK(SQL_SUCCESS, rc,szSQLGETINFO);

return((UWORD)_ttoi(_tcstok(szNum,TEXT("."))));

} /* GetDMVersion() */


//-----------------------------------------------------------------------
// Function: DisplayTestName
//-----------------------------------------------------------------------

VOID PASCAL DisplayTestName(UWORD i)
{
szWrite(szBLANKLINE,TRUE);
szWrite(szTAB,FALSE);
szWrite(iTestNames[i-1], TRUE);

} /* DisplayTestName() */



/*************************** External Interfaces *************************/
/* These functions are called by Gator to envoke the tests. */
/***************************************************************************/

BOOL CALLBACK AutoTestName(LPTSTR szName, UINT * cbTestCases)
{
static TCHAR szTestName[] = TEXT("Quick Test");

lstrcpy(szName, szTestName);

*cbTestCases = sizeof(iTestNames)/sizeof(LPTSTR);

return TRUE;
}

//-----------------------------------------------------------------------
// Function: AutoTestDesc
// Purpose: Provides gator with a list of tests which can be run.
//-----------------------------------------------------------------------
BOOL CALLBACK AutoTestDesc(UWORD iTest, LPTSTR szName, LPTSTR szDesc)
{

UWORD len=0;

if (iTest > (sizeof(iTestNames)/sizeof(LPTSTR)))
return FALSE;

if (szName)
lstrcpy(szName,iTestNames[iTest-1]);

if (szDesc)
lstrcpy(szDesc,TEXT(" "));

return TRUE;

}

//-----------------------------------------------------------------------
// Function: AutoTestFunc
//-----------------------------------------------------------------------

void CALLBACK AutoTestFunc(lpSERVERINFO pTestSource)
{

RETCODErc;
UWORDcTypes;
UDWORDdwLen;
UWORDfIndex;
SDWORDcColsSelected;
FIELDINFO*rgFields = (FIELDINFO *)AllocateMemory(sizeof(FIELDINFO) * MAX_TYPES_SUPPORTED);
QTSTRUCT*lpqt = (QTSTRUCT *)AllocateMemory(sizeof (QTSTRUCT));
DSTRUCT*lpd = (DSTRUCT *)AllocateMemory(sizeof (DSTRUCT) * MAX_TYPES_SUPPORTED);
UWORDuDMVer;
SDWORDcbValue=0;
TCHARszAPI[MEDBUFF];
UWORDi;
TCHARszState[100]=TEXT(""),
szErrMsg[XLARGEBUFF]=TEXT("");


lpSI = pTestSource;
lpSI->failed = 0;
henv = NULL;
hdbc = NULL;
hstmt = NULL;
hdesc = NULL;

if(lpSI->szValidServer0)
{
if (Connect(lpSI,lpqt))
goto ErrorRet;
}
else
{
henv = lpSI->henv;
hdbc = lpSI->hdbc;

rc = SQLSetConnectOption(hdbc,SQL_ODBC_CURSORS,dwLen);
lstrcpy(szAPI,szSQLSETCONNECTOPTION);

if(RETCHECK(SQL_SUCCESS, rc,szAPI))
lpSI->vCursorLib = dwLen;
}

uDMVer=GetDMVersion();

fIndex=CheckConformanceLevel();

fBindParameter=Supported(SQL_API_SQLBINDPARAMETER);
fDiagRecSupported=Supported(SQL_API_SQLGETDIAGREC);
fDiagFieldSupported=Supported(SQL_API_SQLGETDIAGFIELD);


AllocHstmt();

if (!(cTypes=GetTypeInfo(rgFields,lpqt)))
goto ErrorRet;

guwRowCount=cTypes+1;// Extra row inserted with DAE.

// cTypes=20; //DEBUG

/* build create statement */
if (BuildCreateStmt(lpqt,rgFields,cTypes))
goto ErrorRet;

/* put together the insert statement, and set the parameters */
/* parameters are only set the first time through, after which */
/* the contents of the pointers is changed */
if (BuildInsertStmt(lpqt, rgFields,cTypes,lpd,&cColsSelected,fBindParameter))
goto ErrorRet;

CreateParamQuery(lpqt,rgFields,cTypes);

for(i=1;i <= (sizeof(iTestNames)/sizeof(LPTSTR));i++)
{
lpqt->sz[0] = TEXT('\0');

if(GETBIT(pTestSource->rglMask,i))
{
/* Display current Test */
DisplayTestName(i);

switch(i)
{
case 1:
TestConnectionOptions();
break;
case 2:
TestStmtOptions();
break;
case 3:
TestCursorName(lpqt);
break;
case 4:
TestData(lpqt,rgFields,cTypes,lpd);
break;
case 5:
TestNumResCols(cColsSelected,lpqt);
break;
case 6:
TestMetaData(lpSI,lpqt,rgFields,cTypes);
break;
case 7:
TestSearchedQuery(lpqt,rgFields,cTypes,lpd,fBindParameter);
break;
case 8:
TestLargeQuery(lpqt,rgFields,cTypes,lpd);
break;
case 9:
TestSQLTables(lpqt);
break;
case 10:
TestSQLStatistics(lpqt,rgFields,cTypes,lpd,fIndex);
break;
case 11:
TestSQLSpecialCols(lpqt);
break;
case 12:
TestLikeQuery(lpqt,rgFields,cTypes,lpd);
break;
case 13:
TestSQLForeignKeys(lpqt);
break;
case 14:
TestSQLBrowseConnect(lpSI);
break;
case 15:
TestSQLDataSources();
break;
case 16:
TestSQLDrivers();
break;
case 17:
TestSQLMoreResults(lpqt);
break;
case 18:
TestSQLNativeSQL(lpqt);
break;
case 19:
TestSQLDescribeParam(lpqt);
break;
case 20:
TestSQLNumParams(lpqt);
break;
case 21:
TestSQLParamOptions(lpqt);
break;
case 22:
TestSQLPrimaryKeys(lpqt);
break;
case 23:
TestSQLProcedures();
break;
case 24:
TestSQLTablePrivileges();
break;
case 25:
TestSQLColumnPrivileges(lpqt);
break;
case 26:
TestSQLSetScrollOptions();
break;
case 27:
TestExtendedFetch(lpqt,rgFields,cTypes);
break;
case 28:
TestOJCap(lpqt);
break;
case 29:
TestSQLSetConnectAttr();
break;
case 30:
TestSQLSetStmtAttr();
break;
case 31:
TestThreading(lpSI,lpqt,rgFields);
break;
case 32:
TestGetDescField(lpqt);
break;
case 33:
TestSetDescField(lpqt);
break;
case 34:
TestGetDescRec(lpqt);
break;
case 35:
TestSetDescRec(lpqt);
break;
case 36:
TestCopyDesc(lpqt);
break;
case 37:
TestDescDefaults(lpqt);
break;
case 38:
TestUseDesc(lpqt, rgFields);
break;
case 39:
TestEnvAttr();
break;
case 40:
TestEndTran(lpqt,rgFields,cTypes,lpd);
break;
case 41:
TestBindParam(lpqt,rgFields,cTypes,lpd);
break;
case 42:
TestQuickRebind(lpqt,rgFields,cTypes,lpd);
break;
case 43:
TestFetchScroll(lpqt);
break;
case 44:
TestDiagRec();
break;
case 45:
TestDiagField();
break;
case 46:
TestMixedAnsiUnicode(lpqt);
break;

} /* switch(i) */

} /* if(GETBIT(pTestSource->rglMask,i)) */

}

//the end of test cases for ODBC SDK 3.0

/* finished testing, clean up */
CleanUp(lpd,lpqt,rgFields,lpqt->szTableName,FALSE,lpSI->szValidServer0);

szWrite(szBLANKLINE,TRUE);

lpSI->cErrors = lpSI->failed;

return;

ErrorRet:
/* a failure in an ODBC function that prevents completion of the*/
/* test - for example, connect to the server */

szWrite(TEXT("\t\t *** Unrecoverable Quick Test FAILURE ***"), TRUE);

/* finished testing, clean up */
CleanUp(lpd,lpqt,rgFields,lpqt->szTableName,FALSE,lpSI->szValidServer0);

lpSI->cErrors = ABORT;

return;
}