/* Copyright (c) 2003, 2005, 2007 MySQL AB
   Use is subject to license terms

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */

// ODBC.cpp : Defines the entry point for the console application.
//

#include "SQL99_test.h"
#include <iostream> // Loose later

using namespace std; //

#define MAXCOL			64
#define DEFCOL			4

#define MAXROW			64
#define DEFROW			8

/*
  NDB_MAXTHREADS used to be just MAXTHREADS, which collides with a
  #define from <sys/thread.h> on AIX (IBM compiler).  We explicitly
  #undef it here lest someone use it by habit and get really funny
  results.  K&R says we may #undef non-existent symbols, so let's go.
*/
#undef MAXTHREADS
#define NDB_MAXTHREADS		24
#define DEFTHREADS		2

#define MAXTABLES		16
#define DEFTABLES		2

#define MAXLOOPS		100000
#define DEFLOOPS		4

#define UPDATE_VALUE	7

#define PKSIZE 2


static int nNoOfThreads = 1 ;
static int nNoOfCol = 4 ;
static int nNoOfRows = 2 ;
static int nNoOfLoops = 0 ;
static int nNoOfTables = 2 ;
static int nAPI = 0 ;
static int tAttributeSize = sizeof(char) ;
static attr_type AttributeType = T_CHAR ;
static int nAggregate = 0 ;
static int nArithmetic = 0 ;
static int nPrint = 0 ;
static int nColList = 0 ;
static char szColNames[MAXCOL*MAX_COL_NAME] = { 0 } ;
int createTables(char* szTableName, int nTables) ;


/*************************************************
Function: main - the entry point
*************************************************/
int main(int argc, char* argv[]){	

	int nRetrunValue = NDBT_FAILED ;
	SQLRETURN rc = SQL_ERROR ;
	double dResultA = 0 ;
    double dResultB = 0 ;
    double dInput = 0 ;
    int x = 0, y = 0 ;
    int* pIntRefBuffer = NULL ;
    float* pFloatRefBuffer = NULL ;
    double*	pDoubleRefBuffer = NULL ;
    char* pCharRefBuffer = NULL ;
	char szColBuffer[MAX_COL_NAME] = { 0 } ;


    ParseArguments(argc, (const char**)argv) ;

    PARAMS* pparams = (PARAMS*)malloc(sizeof(PARAMS)*nNoOfThreads) ;
    memset(pparams, 0, (sizeof(PARAMS)*nNoOfThreads)) ;

    char* szTableNames = (char*)malloc(sizeof(char)*nNoOfTables*MAX_TABLE_NAME) ;
    memset(szTableNames, 0, sizeof(char)*nNoOfTables*MAX_TABLE_NAME) ;

	UintPtr pThreadHandles[NDB_MAXTHREADS] = { NULL } ;

    AssignTableNames(szTableNames, nNoOfTables) ;

	if(nAPI){
		if(0 != createTables(szTableNames, nNoOfTables)){
			printf("Failed to create tables through NDB API; quitting...\n") ;
			NDBT_ProgramExit(NDBT_FAILED) ;
			return NDBT_FAILED ;
		}
	}else{
	
		//CreateDemoTables(szTableNames, nNoOfTables, DROP) ;
		rc = CreateDemoTables(szTableNames, nNoOfTables, CREATE) ;
		if(!(SQL_SUCCESS == rc || SQL_SUCCESS_WITH_INFO == rc)){
			printf("Failed to create tables, quiting now.\n") ;
			NDBT_ProgramExit(NDBT_FAILED) ;
			return NDBT_FAILED ;
		}
	}

	// Store column names in the buffer for use in some stmts
	int k = 0 ;
	for(;;){
		memset((char*)szColBuffer, 0, strlen(szColBuffer)) ;
		sprintf((char*)szColBuffer, "COL%d", k) ;
        strcat((char*)szColNames, (char*)szColBuffer) ;
        ++k ;
		if( k == nNoOfCol ){
			break ;
		}	
		strcat((char*)szColNames, ", ") ;
	} // for


    switch(AttributeType){
        case T_INT:
            pIntRefBuffer = (int*)malloc(sizeof(int)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
            memset(pIntRefBuffer, 0, sizeof(int)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
            AssignRefNumValues(pIntRefBuffer, T_INT, nPrint) ;
            StartThreads(pparams, (void*)pIntRefBuffer, nNoOfTables, szTableNames, AttributeType, pThreadHandles) ;
            break ;
        case T_FLOAT:
            pFloatRefBuffer = (float*)malloc(sizeof(float)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
            memset(pFloatRefBuffer, 0, sizeof(float)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
            AssignRefNumValues(pFloatRefBuffer, T_FLOAT, nPrint) ;
            StartThreads(pparams, (void*)pFloatRefBuffer, nNoOfTables, szTableNames, AttributeType, pThreadHandles) ;
            break ;
/*        case T_DOUBLE:
            pDoubleRefBuffer = (double*)malloc(sizeof(double)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
            memset(pDoubleRefBuffer, 0, sizeof(double)*nNoOfRows*nNoOfCol*nNoOfThreads) ;
            AssignRefNumValues(pDoubleRefBuffer, T_DOUBLE, 0) ;
            StartThreads(pparams, (void*)pDoubleRefBuffer, nNoOfTables, szTableNames, AttributeType, pThreadHandles) ;
            break ;
*/
        case T_CHAR:
            pCharRefBuffer = (char*)malloc(sizeof(char)*nNoOfRows*nNoOfCol*nNoOfThreads*MAX_CHAR_ATTR_LEN) ;
            memset(pCharRefBuffer, 0, sizeof(char)*nNoOfRows*nNoOfCol*nNoOfThreads*MAX_CHAR_ATTR_LEN) ;
            AssignRefCharValues(pCharRefBuffer, nPrint ) ;
            StartThreads(pparams, (void*)pCharRefBuffer, nNoOfTables, szTableNames, AttributeType, pThreadHandles) ;
            break ;
        default:
            break ;
        }

	NdbThread_SetConcurrencyLevel(nNoOfThreads + 2) ;


	printf("\nPerforming inserts...") ;
	SetThreadOperationType(pparams, T_INSERT) ;
	if(0 < WaitForThreads(pparams)){
		printf("\t\t%d thread(s) failed\n") ;
	}else{
		printf("\t\tdone\n") ;
	}
	printf("----------------------\n\n") ;
	PrintAll(szTableNames, nNoOfTables, AttributeType) ;

	printf("\nVerifying inserts...") ;
	SetThreadOperationType(pparams, T_READ_VERIFY) ;
	if(0 < WaitForThreads(pparams)){
		printf("\t\t%d thread(s) failed\n") ;
	}else{
		printf("\t\tdone\n") ;
	}
	printf("----------------------\n\n") ;

	printf("\nPerforming updates...") ;
	SetThreadOperationType(pparams, T_UPDATE) ;
	if(0 < WaitForThreads(pparams)){
		printf("\t\t%d thread(s) failed\n") ;
	}else{
		printf("\t\tdone\n") ;
	}
	printf("----------------------\n\n") ;
	//PrintAll(szTableNames, nNoOfTables, AttributeType) ;

	printf("\nVerifying updates...") ;
	SetThreadOperationType(pparams, T_READ_VERIFY) ;
	if(0 < WaitForThreads(pparams)){
		printf("\t\t%d thread(s) failed\n") ;
	}else{
		printf("\t\tdone\n") ;
	}
	printf("----------------------\n\n") ;

	printf("\nPerforming reads...") ;
	SetThreadOperationType(pparams, T_READ) ;
	if(0 < WaitForThreads(pparams)){
		printf("\t\t%d thread(s) failed\n") ;
	}else{
		printf("\t\tdone\n") ;
	}
	printf("----------------------\n\n") ;
	PrintAll(szTableNames, nNoOfTables, AttributeType) ;


	if(T_CHAR != AttributeType && nAggregate){
		printf("\nTesting aggregate functions for each table\n\n") ;
		printf("FN\tCOLUMN\tVALUE\t\t\tTOTAL ROWS WHERE\n\t\t\t\t\tVALUE(S) > VALUE\n--------------------------------------------------------\n\n") ;

		for(y = 0 ; y < nNoOfTables ; ++y){
			for(x = 0; x < nNoOfCol ; ++x){
				dResultA = dResultB = 0 ;
				AggregateFn(FN_MIN, (char*)(szTableNames + MAX_TABLE_NAME*y), x, NULL, &dResultA, AttributeType) ;
				AggregateFn(FN_COUNT, (char*)(szTableNames + MAX_TABLE_NAME*y) , x, &dResultA, &dResultB, AttributeType) ;
				ATTR_TYPE_SWITCH_AGR("MIN", x, dResultA, dResultB, AttributeType) ;	
				}
			}

			for(y = 0; y < nNoOfTables ; ++y){ 
				for(x = 0; x < nNoOfCol ; ++x){
					dResultA = dResultB = 0 ;
					AggregateFn(FN_MAX, (char*)(szTableNames + MAX_TABLE_NAME*y), x, NULL, &dResultA, AttributeType) ;
					AggregateFn(FN_COUNT, (char*)(szTableNames + MAX_TABLE_NAME*y), x, &dResultA, &dResultB, AttributeType) ;
					ATTR_TYPE_SWITCH_AGR("MAX", x, dResultA, dResultB, AttributeType) ;
					}
				}

			for(y = 0 ; y < nNoOfTables ; ++y){
				for(x = 0; x < nNoOfCol ; ++x){
					dResultA = dResultB = 0 ;
					AggregateFn(FN_AVG, (char*)(szTableNames + MAX_TABLE_NAME*y), x, NULL, &dResultA, AttributeType) ;
					AggregateFn(FN_COUNT, (char*)(szTableNames + MAX_TABLE_NAME*y), x, &dResultA, &dResultB, AttributeType) ;
					ATTR_TYPE_SWITCH_AGR("AVG", x, dResultA, dResultB, AttributeType)
					}
			}

			printf("--------------------------------------------------------\n\n") ;
	}

	if(T_CHAR != AttributeType && nArithmetic){

	float nVal = (rand() % 10) /1.82342 ;

		for(int h = 0 ; h < nNoOfTables ; ++h){

			printf("\nTesting arithmetic operators\nfor each column in %s:\n----------------------\n", (char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h) ) ;

			printf("\nOperator [ * ]... \t\t") ;
			ArithOp((char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h), nNoOfCol, &nVal, AttributeType, MULTI) ;
			printf("done\n") ;

			printf("\nOperator [ / ]... \t\t") ;
			ArithOp((char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h), nNoOfCol, &nVal, AttributeType, DIVIDE) ;
			printf("done\n") ;

			printf("\nOperator [ + ]... \t\t") ;
			ArithOp((char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h), nNoOfCol, &nVal, AttributeType, PLUS) ;
			printf("done\n") ;

			printf("\nOperator [ - ]... \t\t") ;
			ArithOp((char*)(szTableNames + MAX_TABLE_NAME*sizeof(char)*h), nNoOfCol, &nVal, AttributeType, MINUS) ;
			printf("done\n\n") ;
			/*	
			printf("\nOperator [ % ]... \t\t") ;
			ArithOp((char*)szTableNames, nNoOfCol, &nVal, AttributeType, MODULO) ;
			printf("done\n\n") ;
			*/	
		}
	}
/*
	printf("\nPerforming deletes...") ;
	SetThreadOperationType(pparams, T_DELETE) ;
	if(0 < WaitForThreads(pparams)){
		printf("\t\t%d thread(s) failed\n") ;
	}else{
		printf("\t\tdone\n") ;
	}
	printf("----------------------\n\n") ;

	printf("\nVerifying deletes...") ;
	SetThreadOperationType(pparams, T_DELETE_VERIFY) ;
	if(0 < WaitForThreads(pparams)){
		printf("\t\t%d thread(s) failed\n") ;
	}else{
		printf("\t\tdone\n") ;
	}
	printf("----------------------\n\n") ;
*/
	StopThreads(pparams, pThreadHandles) ;

	//PrintAll(szTableNames, nNoOfTables, AttributeType) ;

	//CreateDemoTables(szTableNames, nNoOfTables, DROP) ;

	free((void*)szTableNames) ;
	free((void*)pparams) ;
	free((void*)pIntRefBuffer) ;
	free((void*)pFloatRefBuffer) ;
	free((void*)pDoubleRefBuffer) ;
	free((void*)pCharRefBuffer) ;

	return 0;
}



/**************************************************
Function: ParseArguments
***************************************************/
void ParseArguments(int argc, const char** argv){

    int i = 1;

    while (argc > 1){

        if (strcmp(argv[i], "-t") == 0)
            {
            nNoOfThreads = atoi(argv[i+1]);
            if ((nNoOfThreads < 1) || (nNoOfThreads > NDB_MAXTHREADS))
                nNoOfThreads = DEFTHREADS ;
            }
        else if (strcmp(argv[i], "-c") == 0)
            {
            nNoOfCol = atoi(argv[i+1]);
            if ((nNoOfCol < 2) || (nNoOfCol > MAXCOL))
                nNoOfCol = DEFCOL ;
            }
        else if (strcmp(argv[i], "-l") == 0)
            {
            nNoOfLoops = atoi(argv[i+1]);
            if ((nNoOfLoops < 0) || (nNoOfLoops > MAXLOOPS))
                nNoOfLoops = DEFLOOPS ;
            }
        else if (strcmp(argv[i], "-r") == 0)
            {
            nNoOfRows = atoi(argv[i+1]);;
            if ((nNoOfRows < 0) || (nNoOfRows > MAXROW))
                nNoOfRows = DEFROW ;
            }
		else if (strcmp(argv[i], "-m") == 0)
            {
            nArithmetic = 1 ;
			argc++ ;
			i-- ;
            }
		else if (strcmp(argv[i], "-g") == 0)
            {
            nAggregate = 1 ;
			argc++ ;
			i-- ;
            }
		else if (strcmp(argv[i], "-n") == 0)
            {
			nAPI = 1 ;
			argc++ ;
			i-- ;
            }
		else if (strcmp(argv[i], "-v") == 0)
            {
			nPrint = 1 ;
			argc++ ;
			i-- ;
            }
        else if (strcmp(argv[i], "-a") == 0)
            {
            if(strcmp(argv[i+1], "int") == 0){
                AttributeType = T_INT ;
				tAttributeSize = 32 ;
                }else if(strcmp(argv[i+1], "float") == 0){
                    AttributeType = T_FLOAT ;
					tAttributeSize = 64 ;
                    }else if(strcmp(argv[i+1], "char") == 0){
                        AttributeType = T_CHAR ;
                        }
            }
        else
            {
            cout << "Arguments:\n";
			cout << "-n Create tables using NDB API (vs ODBC by default)" << endl;
            cout << "-t Number of threads; maximum 24, default 2\n" << endl;
            cout << "-c Number of columns per table; maximum 64, default 4\n" << endl;
            cout << "-r Number of rows; maximum 64, default 8\n" << endl;
            cout << "-a Type of attribute to use: int, double or char; default int " << endl;
            cout << "-g Test aggregate functions" << endl;
			cout << "-m Test arithmetic operators" << endl;
			cout << "-v Print executed statements" << endl;
            exit(-1);
            }

        argc -= 2 ;
        i = i + 2 ;
        }

char *szAttrType[MAX_STR_LEN] = { 0 } ;
switch(AttributeType){
        case T_INT:
            strcpy((char*)szAttrType, "Integer") ;
            break ;
        case T_FLOAT:
            strcpy((char*)szAttrType, "Float") ;
            break ;
/*        case T_DOUBLE:
            strcpy((char*)szAttrType, "Double") ;
            break ;
*/
        case T_CHAR:
            strcpy((char*)szAttrType, "Character") ;
            break ;
        default:
            strcpy((char*)szAttrType, "Not defined") ;
            break ;
    }


printf("\n\nCurrent parameters: %d thread(s), %d tables, %d rows, %d colums, attribute type: %s\n\n", nNoOfThreads, nNoOfTables, nNoOfRows, nNoOfCol, szAttrType) ;
    }




/*************************************************
Function: ThreadFnInt - thread function
for int attributes
*************************************************/
void* ThreadFnInt(void* pparams){

    SQLRETURN retcode = SQL_ERROR ;
    SQLCHAR	szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
    SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
    SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
    SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
    SQLINTEGER cbInt = 0 ;
    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;

    int r = 0, j = 0 ;
    //Get thread parameters 	
    PARAMS* p = (PARAMS*)pparams ;
    int* pRef = (int*)p->pThreadRef ;

    int* pBindBuffer = (int*)malloc(sizeof(int)*nNoOfCol) ;

    //printf("Thread #%d\n", p->nThreadID) ;

    retcode = GetHandles(&stHandles, GET, 0) ;
	
	if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
		p->report_status = S_STARTED ;
	}else{
		printf("Thread #%d failed to allocate handles, exiting now.\n", p->nThreadID) ;
		free((void*)pBindBuffer) ;
		p->nError = 1 ;
		p->report_status = S_EXIT ;
		return 0 ;
	}
    
	//p->report_status = S_STARTED ;

    //Main thread loop
    for(;;){

        while(S_IDLE == p->thread_status){
            NdbSleep_MilliSleep(1) ;
            }

    if(S_STOP == p->thread_status) {
        break ;
        }else{
            p->thread_status = S_BUSY ;
            }

    switch(p->op_type){


        /************************************** T_INSERT case **************************************/
        case T_INSERT:

            for(r = 0 ; r < nNoOfRows ; ++r){

				if(!nColList){
					sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ; 
				}else{
					sprintf((char*)szStmtBuffer, "INSERT INTO %s (%s) VALUES(", p->szTableName, szColNames) ;
				}

                //sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ; 

                for(j = 0 ;;){
                    sprintf((char*)szValueBuffer,"%d", pRef[nNoOfCol*r + j]) ;
                    strncat((char*)szStmtBuffer, (char*)szValueBuffer, strlen((char*)szValueBuffer)) ;
                    ++j ;
                    if(nNoOfCol == j) break ;
                    strcat((char*)szStmtBuffer, ", ") ;
                    }
            strcat((char*)szStmtBuffer, ")") ;
			if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
            retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
			if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
			}else{
				p->nError = 1 ;
				printf("INSERT in thread #%d failed\n", p->nThreadID) ;
				HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
			}
			}
        break ;


        /************************************** T_READ case **************************************/
        case T_READ:

            for(r = 0 ; r < nNoOfRows ; r++){

                sprintf((char*)szStmtBuffer, "SELECT * FROM %s WHERE COL0 = %d", p->szTableName, pRef[nNoOfCol*r]) ;
				if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
                ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS), retcode) ;

                for(j = 0 ; j < nNoOfCol ; ++j){
                    ODBC_FN(SQLBindCol(stHandles.hstmt, (j+1), SQL_C_SLONG, (void*)&pBindBuffer[j], sizeof(SQLINTEGER), &cbInt), retcode) ;
                    }

            for (;;) {
                retcode = SQLFetch(stHandles.hstmt);

                if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
                    for(int k = 0 ; k < nNoOfCol ; ++k){
                        if(p->nVerifyFlag){
                            if(pBindBuffer[k] != pRef[nNoOfCol*r + k])
                                printf("Expected: %d Actual: %d\n", pBindBuffer[k], pRef[nNoOfCol*r + k]) ;
                            }
                        }
                    }else if(SQL_NO_DATA == retcode){
                        break ;
                        }else{
							p->nError = 1 ;
							printf("READ in thread #%d failed\n", p->nThreadID) ;
                            HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
                            }
                    //printf("\n") ;
                }
				SQLCloseCursor(stHandles.hstmt) ;
			}
        break ;


        /************************************** T_UPDATE case **************************************/
        case T_UPDATE:
            for(r = 0 ; r < nNoOfRows ; ++r){

                sprintf((char*)szStmtBuffer, "UPDATE %s SET ", p->szTableName) ; 
                for(j = 1 ;;){
                    pRef[nNoOfCol*r + j] = pRef[nNoOfCol*r + j] + UPDATE_VALUE ;
                    sprintf((char*)szColBuffer,"COL%d = %d",  j, pRef[nNoOfCol*r + j]) ;
                    strncat((char*)szStmtBuffer, (char*)szColBuffer, strlen((char*)szColBuffer)) ;
                    ++j ;
                    if(nNoOfCol == j) break ;
                    strcat((char*)szStmtBuffer, ", ") ;
				}
				sprintf((char*)szAuxBuffer, " WHERE COL0 = %d ;", pRef[nNoOfCol*r]) ;		
				strcat((char*)szStmtBuffer, (char*)szAuxBuffer);
				if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
				retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;

				if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
				}else{
					p->nError = 1 ;
					printf("UPDATE in thread %d failed\n", p->nThreadID) ;
					HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
				}
			}
        break ;


        /************************************** T_DELETE case **************************************/
        case T_DELETE:
            for(r = 0 ; r < nNoOfRows ; ++r){
                sprintf((char*)szStmtBuffer, "DELETE * FROM %s WHERE COL0 = %d", p->szTableName, pRef[nNoOfCol*r]) ;
                if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
				retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
				if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
				}else if( 1 == p->nVerifyFlag  && SQL_NO_DATA != retcode){
					p->nError = 1 ;
					printf("\nVerification failed: the row found\n") ;
                }else{
					p->nError = 1 ;
					printf("INSERT in thread %d failed\n", p->nThreadID) ;
					HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
				}

			}
        break ;


        /************************************** default case **************************************/
        default:
            break ;
        }//switch
p->thread_status = S_IDLE ;
        } //for

free((void*)pBindBuffer) ;
GetHandles(&stHandles, FREE, 0) ;
p->thread_status = S_EXIT ;
return 0 ;	
    };



/*************************************************
Function: ThreadFnFloat - thread function
for float attributes
*************************************************/
void* ThreadFnFloat(void* pparams){

    SQLRETURN retcode = SQL_ERROR ;
    SQLCHAR	szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
    SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
    SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
    SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
    SQLINTEGER cbFloat = 0 ;
    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;

    int r = 0, j = 0 ;
    //Get thread parameters 	
    PARAMS* p = (PARAMS*)pparams ;

    float* pRef = (float*)p->pThreadRef ;
    float* pBindBuffer = (float*)malloc(sizeof(float)*nNoOfCol) ;

    //printf("Thread #%d\n", p->nThreadID) ;

    retcode = GetHandles(&stHandles, GET, 0) ;
	
	if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
		p->report_status = S_STARTED ;
	}else{
		printf("Thread #%d failed to allocate handles, exiting now.\n", p->nThreadID) ;
		free((void*)pBindBuffer) ;
		p->nError = 1 ;
		p->report_status = S_EXIT ;
		return 0 ;
	}

    //p->report_status = S_STARTED ;

    //Main thread loop
    for(;;){

        while(S_IDLE == p->thread_status){
            NdbSleep_MilliSleep(1) ;
            }

    if(S_STOP == p->thread_status) {
        break ;
        }else{
            p->thread_status = S_BUSY ;
            }

    switch(p->op_type){


        /************************************** T_INSERT case **************************************/
        case T_INSERT:

            for(r = 0 ; r < nNoOfRows ; ++r){

				if(!nColList){
					sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ; 
				}else{
					sprintf((char*)szStmtBuffer, "INSERT INTO %s (%s) VALUES(", p->szTableName, szColNames) ;
				}

                //sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ; 

                for(j = 0 ;;){
                    sprintf((char*)szValueBuffer,"%f", pRef[nNoOfCol*r + j]) ;
                    strncat((char*)szStmtBuffer, (char*)szValueBuffer, strlen((const char*)szValueBuffer)) ;
                    ++j ;
                    if(nNoOfCol == j) break ;
                    strcat((char*)szStmtBuffer, ", ") ;
                    }
            strcat((char*)szStmtBuffer, ")") ;
			if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
            retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
			if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
			}else{
				p->nError = 1 ;
				printf("INSERT in thread #%d failed\n", p->nThreadID) ;
				HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
			}
                }
        break ;


        /************************************** T_READ case **************************************/
        case T_READ:

            for(r = 0 ; r < nNoOfRows ; ++r){

                sprintf((char*)szStmtBuffer, "SELECT * FROM %s WHERE COL0 = %f", p->szTableName, pRef[nNoOfCol*r]) ;
				if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
                ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS), retcode) ;

                for(j = 0 ; j < nNoOfCol ; ++j){
                    ODBC_FN(SQLBindCol(stHandles.hstmt, (j+1), SQL_C_FLOAT, (void*)&pBindBuffer[j], sizeof(SQLFLOAT), &cbFloat), retcode) ;
                    }

            for (;;) {
                retcode = SQLFetch(stHandles.hstmt);

                if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
                    for(int k = 0 ; k < nNoOfCol ; ++k){
                        if(p->nVerifyFlag){
                            if(abs(pBindBuffer[k] - pRef[nNoOfCol*r + k]) > FLTDEV )
                                printf("Expected: %f Actual: %f\n", pBindBuffer[k], pRef[nNoOfCol*r + k]) ;
                            }
                        }
                    }else if(SQL_NO_DATA == retcode){
                        break ;
					}else{
						p->nError = 1 ;
						printf("READ in thread #%d failed\n", p->nThreadID) ;
						HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
					}
                    //printf("\n") ;
                }
        SQLCloseCursor(stHandles.hstmt) ;
                }
        break ;


        /************************************** T_UPDATE case **************************************/
        case T_UPDATE:
            for(r = 0 ; r < nNoOfRows ; ++r){

                sprintf((char*)szStmtBuffer, "UPDATE %s SET ", p->szTableName) ; 
                for(j = 1 ;;){
                    pRef[nNoOfCol*r + j] = pRef[nNoOfCol*r + j] + UPDATE_VALUE ;
                    sprintf((char*)szColBuffer,"COL%d = %f",  j, pRef[nNoOfCol*r + j]) ;
                    strncat((char*)szStmtBuffer, (char*)szColBuffer, strlen((char*)szColBuffer)) ;
                    ++j ;
                    if(nNoOfCol == j) break ;
                    strcat((char*)szStmtBuffer, ", ") ;
                    }
            sprintf((char*)szAuxBuffer, " WHERE COL0 = %f ;", pRef[nNoOfCol*r]) ;		
            strcat((char*)szStmtBuffer, (char*)szAuxBuffer);
			if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
			retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
			if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
			}else{
				p->nError = 1 ;
				printf("UPDATE in thread #%d failed\n", p->nThreadID) ;
				HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
			}
			}
        break ;


        /************************************** T_DELETE case **************************************/
        case T_DELETE:
            for(r = 0 ; r < nNoOfRows ; ++r){
                sprintf((char*)szStmtBuffer, "DELETE * FROM %s WHERE COL0 = %f", p->szTableName, pRef[nNoOfCol*r]) ;
                if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
				retcode = SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS) ;
				if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
				}else if( 1 == p->nVerifyFlag && SQL_NO_DATA != retcode){
					p->nError = 1 ;
                    printf("\nVerification failed: still row exists\n") ;
				}else{
					p->nError = 1 ;
					printf("DELETE in thread #%d failed\n", p->nThreadID) ;
					HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
				}
			}
        break ;


        /************************************** default case **************************************/
        default:
            break ;
        }//switch
p->thread_status = S_IDLE ;
        } //for

free((void*)pBindBuffer) ;
GetHandles(&stHandles, FREE, 0) ;
p->thread_status = S_EXIT ;
return 0 ;	
    };



/*************************************************
Function: ThreadFnDouble - thread function
for double attributes
*************************************************/
/*
void* ThreadFnDouble(void* pparams){

    SQLRETURN retcode = SQL_ERROR ;
    SQLCHAR	szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
    SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
    SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
    SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
    SQLINTEGER cbDouble = 0 ;
    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
    int r = 0, j = 0 ;

    //Get thread parameters 	
    PARAMS* p = (PARAMS*)pparams ;
    double* pRef = (double*)p->pThreadRef ;

    double* pBindBuffer = (double*)malloc(sizeof(double)*nNoOfCol) ;

    //printf("Thread #%d\n", p->nThreadID) ;

    retcode = GetHandles(&stHandles, GET, 0) ;
	
	if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
		p->report_status = S_STARTED ;
	}else{
		printf("Thread #%d failed to allocate handles, exiting now.\n", p->nThreadID) ;
		free((void*)pBindBuffer) ;
		p->report_status = S_EXIT ;
		return 0 ;
	}
    //p->report_status = S_STARTED ;

    //Main thread loop
    for(;;){

        while(S_IDLE == p->thread_status){
            NdbSleep_MilliSleep(1) ;
            }

    if(S_STOP == p->thread_status) {
        break ;
        }else{
            p->thread_status = S_BUSY ;
            }

    switch(p->op_type){


  /************************************** T_INSERT case **************************************/
  /*     case T_INSERT:

            for(r = 0 ; r < nNoOfRows ; ++r){	
                sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ; 
                for(j = 0 ;;){
                    sprintf((char*)szValueBuffer,"%.9f", pRef[nNoOfCol*r + j]) ;
                    strncat((char*)szStmtBuffer, (char*)szValueBuffer, strlen((const char*)szValueBuffer)) ;
                    ++j ;
                    if(nNoOfCol == j) break ;
                    strcat((char*)szStmtBuffer, ", ") ;
                    }
            strcat((char*)szStmtBuffer, ")") ;
            ODBC_FN(SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
                }

        break ;


  /************************************** T_READ case **************************************/
  /*      case T_READ:

            for(r = 0 ; r < nNoOfRows ; ++r){
                sprintf((char*)szStmtBuffer, "SELECT * FROM %s WHERE COL0 = %.9f", p->szTableName, pRef[nNoOfCol*r]) ;
                ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS), retcode) ;
                for(j = 0 ; j < nNoOfCol ; ++j){
                    ODBC_FN(SQLBindCol(stHandles.hstmt, (j+1), SQL_C_DOUBLE, (void*)&pBindBuffer[j], sizeof(SQLDOUBLE), &cbDouble), retcode) ;
                    }
            for (;;) {
                retcode = SQLFetch(stHandles.hstmt);

                if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
                    for(int k = 0 ; k < nNoOfCol ; ++k){
                        if(p->nVerifyFlag){
                            if(abs(pBindBuffer[k] - pRef[nNoOfCol*r + k]) > DBLDEV)
                                printf("Expected: %.9f Actual: %.9f\n", pBindBuffer[k], pRef[nNoOfCol*r + k]) ;
                            }
                        }
                    }else if(SQL_NO_DATA == retcode){
                        break ;
                        }else{
                            HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
                            }
                    //printf("\n") ;
                }
        SQLCloseCursor(stHandles.hstmt) ;
                }
        break ;


    /************************************** T_UPDATE case **************************************/
    /*    case T_UPDATE:
            for(r = 0 ; r < nNoOfRows ; ++r){

                sprintf((char*)szStmtBuffer, "UPDATE %s SET ", p->szTableName) ; 
                for(j = 1 ;;){
                    pRef[nNoOfCol*r + j] = pRef[nNoOfCol*r + j] + UPDATE_VALUE ;
                    sprintf((char*)szColBuffer,"COL%d = %.9f",  j, pRef[nNoOfCol*r + j]) ;
                    strncat((char*)szStmtBuffer, (char*)szColBuffer, strlen((char*)szColBuffer)) ;
                    ++j ;
                    if(nNoOfCol == j) break ;
                    strcat((char*)szStmtBuffer, ", ") ;
                    }
            sprintf((char*)szAuxBuffer, " WHERE COL0 = %.9f ;", pRef[nNoOfCol*r]) ;		
            strcat((char*)szStmtBuffer, (char*)szAuxBuffer);	
            ODBC_FN(SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
                }
        break ;


  /************************************** T_DELETE case **************************************/
  /*      case T_DELETE:
            for(r = 0 ; r < nNoOfRows ; ++r){
                sprintf((char*)szStmtBuffer, "DELETE FROM %s WHERE COL0 = %.9f", p->szTableName, pRef[nNoOfCol*r]) ;
                retcode = SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS) ;
                if( 1 == p->nVerifyFlag && SQL_NO_DATA != retcode ){
                    printf("\nVerification failed: still row exists\n") ;
                    }
                }
        break ;


    /************************************** default case **************************************/
    /*    default:
            break ;
        }//switch
p->thread_status = S_IDLE ;
        } //for

free((void*)pBindBuffer) ;
GetHandles(&stHandles, FREE, 0) ;
p->thread_status = S_EXIT ;
return 0 ;	
    };



/*************************************************
Function: ThreadFnChar - thread function
for character attributes
*************************************************/
void* ThreadFnChar(void* pparams){

    SQLRETURN retcode = SQL_ERROR ;
    SQLCHAR	szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
    SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
    SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
    SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
    SQLINTEGER cbChar = 0 ;
    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
    int r = 0, j = 0 ;

    //Get thread parameters 	
    PARAMS* p = (PARAMS*)pparams ;
    char* pRef = (char*)p->pThreadRef ;
    char* pBindBuffer = (char*)malloc(sizeof(char)*nNoOfCol*MAX_CHAR_ATTR_LEN) ;

    //printf("Thread #%d\n", p->nThreadID) ;

    retcode = GetHandles(&stHandles, GET, 0) ;
	
	if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
		p->report_status = S_STARTED ;
	}else{
		printf("Thread #%d failed to allocate handles, retcode = %d, exiting now.\n", p->nThreadID, retcode) ;
		p->nError = 1 ;
		free((void*)pBindBuffer) ;
		p->report_status = S_EXIT ;
		return 0 ;
	}

    //Main thread loop
    for(;;){
		
		while(S_IDLE == p->thread_status){
            NdbSleep_MilliSleep(1) ;
		}

		if(S_STOP == p->thread_status) {
			break ;
        }else{
            p->thread_status = S_BUSY ;
		}

		switch(p->op_type){


        /************************************** T_INSERT case **************************************/
        case T_INSERT:

            for(r = 0 ; r < nNoOfRows ; ++r){
                memset(szStmtBuffer, 0, strlen(szStmtBuffer)) ;
				if(!nColList){
					sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ; 
				}else{
					sprintf((char*)szStmtBuffer, "INSERT INTO %s (%s) VALUES(", p->szTableName, szColNames) ;
				}

                for(j = 0 ;;){
                    sprintf((char*)szValueBuffer,"'%s'", (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + j*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
                    strncat((char*)szStmtBuffer, (char*)szValueBuffer, strlen((const char*)szValueBuffer)) ;
                    ++j ;
                    if(nNoOfCol == j) break ;
                    strcat((char*)szStmtBuffer, ", ") ;
                    }
            strcat((char*)szStmtBuffer, ")") ;
			
			if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
			retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
			if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
			}else{
				p->nError = 1 ;
				printf("INSERT in thread #%d failed\n", p->nThreadID) ;
				HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
			}
			}

        break ;


        /************************************** T_READ case **************************************/
        case T_READ:

            for(r = 0 ; r < nNoOfRows ; ++r){
                sprintf((char*)szStmtBuffer, "SELECT * FROM %s WHERE COL0 = '%s'", p->szTableName, (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
       			if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
				ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmtBuffer, SQL_NTS), retcode) ;
                for(j = 0 ; j < nNoOfCol ; ++j){
                    ODBC_FN(SQLBindCol(stHandles.hstmt, (j+1), SQL_C_CHAR, (void*)(pBindBuffer+j*MAX_CHAR_ATTR_LEN*sizeof(char)), MAX_CHAR_ATTR_LEN, &cbChar), retcode) ;
                    }
            for (;;) {
				retcode = SQLFetch(stHandles.hstmt);

                if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
					for(int k = 0 ; k < nNoOfCol ; ++k){
						if(p->nVerifyFlag){
                            if(!strcmp((char*)(pBindBuffer + k*MAX_CHAR_ATTR_LEN*sizeof(char)), (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + k*MAX_CHAR_ATTR_LEN*sizeof(char))))
                                printf("Expected: %s Actual: %s\n", (char*)(pBindBuffer + k*MAX_CHAR_ATTR_LEN*sizeof(char)), (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + k*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
                            }
					}
				}else if(SQL_NO_DATA == retcode){
					break ;
				}else{
					p->nError = 1 ;
					printf("READ in thread #%d failed\n", p->nThreadID) ;
					HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
				}
                    //printf("\n") ;
			}
			SQLCloseCursor(stHandles.hstmt) ;
			}
        break ;


        /************************************** T_UPDATE case **************************************/
        case T_UPDATE:
            for(r = 0 ; r < nNoOfRows ; ++r){
                sprintf((char*)szStmtBuffer, "UPDATE %s SET ", p->szTableName) ; 
                for(j = 1 ;;){
                    swab((char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + j*MAX_CHAR_ATTR_LEN*sizeof(char)), (char*)szColBuffer, MAX_CHAR_ATTR_LEN*sizeof(char)) ;
                    memcpy((void*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + j*MAX_CHAR_ATTR_LEN*sizeof(char)), (void*)szColBuffer, MAX_CHAR_ATTR_LEN*sizeof(char)) ;					
                    sprintf((char*)szColBuffer,"COL%d = '%s'",  j, (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char) + j*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
                    strncat((char*)szStmtBuffer, (char*)szColBuffer, strlen((char*)szColBuffer)) ;
                    ++j ;
                    if(nNoOfCol == j) break ;
                    strcat((char*)szStmtBuffer, ", ") ;
                    }
            sprintf( (char*)szAuxBuffer, " WHERE COL0 = '%s';", (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char)) ) ;		
            strcat((char*)szStmtBuffer, (char*)szAuxBuffer) ;
			if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
            retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
			if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
			}else{
				p->nError = 1 ;
				printf("UPDATE in thread #%d failed\n", p->nThreadID) ;
				HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
			}
			}
        break ;


        /************************************** T_DELETE case **************************************/
        case T_DELETE:
            for(r = 0 ; r < nNoOfRows ; ++r){
                sprintf((char*)szStmtBuffer, "DELETE FROM %s WHERE COL0 = '%s\'", p->szTableName, (char*)(pRef + nNoOfCol*r*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
            	if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
				retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
				if(SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
				}else if(1 == p->nVerifyFlag && SQL_NO_DATA != retcode){
                    p->nError = 1 ;
					printf("\nVerification failed: still row exists\n") ;
				}else{
					p->nError = 1 ;
					printf("INSERT in thread #%d failed\n", p->nThreadID) ;
					HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
                }
			}
        break ;


        /************************************** default case **************************************/
        default:
            break ;
		}//switch
	p->thread_status = S_IDLE ;
	} //for

	free((void*)pBindBuffer) ;
	GetHandles(&stHandles, FREE, 0) ;
	p->thread_status = S_EXIT ;
	return 0 ;	
};



/*************************************************
Function: CreateDemoTable
*************************************************/
SQLRETURN CreateDemoTables(char* szTableName, int nTables, table_opt op){

    SQLRETURN retcode = SQL_ERROR ;
    SQLCHAR	szStmtBuffer[MAX_SQL_STMT] = { 0 } ;
    SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
	SQLCHAR szAuxBuffer[32] = { 0 } ;
    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
    int c = 0  ;
	
	GetHandles(&stHandles, GET, 0) ;

    if(CREATE == op){
		
        for(c = 0; c < nTables ; ++c){
			sprintf((char*)szStmtBuffer, "CREATE TABLE %s (", (char*)(szTableName+MAX_TABLE_NAME*c)) ;
            int j = 0 ;
			for(;;){
				sprintf((char*)szColBuffer, "COL%d ", j) ;
                strcat((char*)szStmtBuffer, (char*)szColBuffer) ;
                ++j ;

                switch(AttributeType){
                    case T_INT:
                        strcat((char*)szStmtBuffer, "INTEGER") ;
                        break ;
                    case T_FLOAT:
                        strcat((char*)szStmtBuffer, "FLOAT") ;
                        break ;

/*                    case T_DOUBLE:
                        strcat((char*)szStmtBuffer, "DOUBLE") ;
                        break ;
*/
                    case T_CHAR:
						sprintf((char*)szAuxBuffer, "CHAR(%d)", MAX_CHAR_ATTR_LEN) ;
						strcat((char*)szStmtBuffer,  (char*)szAuxBuffer) ;
                        break ;
                    default:
                        break ;
				}

				if(nNoOfCol <= j){	
					strcat((char*)szStmtBuffer, ")") ;
					break ;
				}	
				strcat((char*)szStmtBuffer, ", ") ;				
            } //for(;;)
			
			
			if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
			ODBC_FN(SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
			if(SQL_SUCCESS != retcode) HandleError(stHandles.hstmt , SQL_HANDLE_STMT) ;

			
		}// for()

	}else{
		
		for(c = 0 ; c < nTables ; ++c){
			sprintf((char*)szStmtBuffer, "DROP TABLE %s ", (char*)(szTableName + MAX_TABLE_NAME*c)) ;
			//ODBC_FN(SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;
			if(nPrint) printf("\n> %s\n", szStmtBuffer) ;
			retcode = SQLExecDirect(stHandles.hstmt, szStmtBuffer, SQL_NTS) ;
		}

	}

    GetHandles(&stHandles, FREE, 0) ;

    return retcode ;
}



/*************************************************
Function: AssignTableNames()
*************************************************/
inline void AssignTableNames(char* szBuffer, int nTables){
    for(int c = 0 ; c < nTables ; ++c){
        sprintf((char*)(szBuffer + MAX_TABLE_NAME*sizeof(char)*c), "TAB%d", c) ;
        }
return ;
    }




/*************************************************
Function: StartThreads()
*************************************************/

inline void StartThreads(PARAMS* p, void* pRef, int nTables, char* szTables, attr_type attrType, UintPtr* pHandles) {

    int* pInt = NULL ;
    float* pFloat = NULL ;
    double* pDouble = NULL ;
    char* pChar = NULL ;
	UintPtr pTmpThread = NULL ;

    bool bFlap = 1 ;
    for(int f = 0 ; f < nNoOfThreads ; ++f){
        p[f].nThreadID = f ;
        p[f].nError = 0 ;
        p[f].thread_status = S_IDLE ;
        p[f].op_type = T_WAIT ;
        if(bFlap){
			strncpy((char*)p[f].szTableName, (char*)szTables, MAX_TABLE_NAME) ;
		}else{
            strncpy((char*)p[f].szTableName, (char*)(szTables + MAX_TABLE_NAME*sizeof(char)), MAX_TABLE_NAME) ;
		}
        bFlap = !bFlap ;
		//pTmpThread = pHandles[ ;

        switch(attrType){
            case T_INT:
                pInt = (int*)pRef ;
                p[f].pThreadRef = (void*)&pInt[nNoOfRows*nNoOfCol*f] ;
                pHandles[f] = (UintPtr)NdbThread_Create(ThreadFnInt, (void**)&p[f], 32768, "SQL99_test", NDB_THREAD_PRIO_MEAN) ;
                break ;
            case T_FLOAT:
                pFloat = (float*)pRef ;
                p[f].pThreadRef = (void*)&pFloat[nNoOfRows*nNoOfCol*f] ;
                pHandles[f] = (UintPtr)NdbThread_Create(ThreadFnFloat, (void**)&p[f], 32768, "SQL99_test", NDB_THREAD_PRIO_MEAN) ;
                break ;
            /*
			case T_DOUBLE:
                pDouble = (double*)pRef ;
                p[f].pThreadRef = (void*)&pDouble[nNoOfRows*nNoOfCol*f] ;
                pHandles[f] = (UintPtr)NdbThread_Create(ThreadFnDouble, (void**)&p[f], 32768, "SQL99_test", NDB_THREAD_PRIO_MEAN) ;
                break ;
			*/
            case T_CHAR:
                pChar = (char*)pRef ;
                p[f].pThreadRef = (void*)&pChar[nNoOfRows*nNoOfCol*f*MAX_CHAR_ATTR_LEN] ;
                pHandles[f] = (UintPtr)NdbThread_Create(ThreadFnChar,  (void**)&p[f], 32768, "SQL99_test", NDB_THREAD_PRIO_MEAN) ;
            default:
                break ;
            }
    while(!(S_STARTED != p[f].report_status || S_EXIT != p[f].report_status)){
        NdbSleep_MilliSleep(1) ;
	}
        }
	return ;
}



/*************************************************
Function: SetThreadOperationType()
*************************************************/
inline void SetThreadOperationType(PARAMS* p, type op){

    for(int e = 0 ; e < nNoOfThreads ; ++e){	
        p[e].nVerifyFlag = 0 ;
        if(T_READ_VERIFY == op){
            p[e].nVerifyFlag = 1 ;
            p[e].op_type = T_READ ;
            }else if(T_DELETE_VERIFY == op){
                p[e].nVerifyFlag = 1 ;
                p[e].op_type = T_DELETE ;
                }else{
                    p[e].op_type = op ;
                    }
            p[e].thread_status = S_GET_BUSY ;
        }
return ;
    }



/*************************************************
Function: WaitForThreads()
*************************************************/
inline int WaitForThreads(PARAMS* p) {
	
	int ret_value = 0 ;
    for(int w = 0 ; w < nNoOfThreads ; ++w){
        while(!(S_IDLE != p[w].thread_status || S_EXIT != p[w].report_status)) {
            NdbSleep_MilliSleep(1) ;
		}
		ret_value += p[w].nError ;
	}
	return ret_value ;
}



/*************************************************
Function: StopThreads()
*************************************************/
inline void StopThreads(PARAMS* p, UintPtr* pHandles) {

    for(int k = 0 ; k < nNoOfThreads ; ++k){
		while(!(S_IDLE != p[k].thread_status || S_EXIT != p[k].report_status)){
			NdbSleep_MilliSleep(1) ;
		}
		p[k].thread_status = S_STOP ;
		while(!(S_EXIT != p[k].thread_status || S_EXIT != p[k].report_status)){
			NdbSleep_MilliSleep(1) ;
		}
		NdbThread_Destroy((NdbThread**)&pHandles[k]) ;
	}

	return ;
}



/*************************************************
Function: PrintAll()
*************************************************/
inline void PrintAll(char* szTableName, int nTables, attr_type attrType){

    SQLRETURN retcode = SQL_ERROR ;
    SQLCHAR* szStmt[MAX_SQL_STMT] = { 0 } ;
    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
    double* pDoubleBuffer = NULL ;
    char* pCharBuffer = NULL ;

    if(T_CHAR != attrType){
        pDoubleBuffer = (double*)malloc(sizeof(double)*nNoOfCol) ;
	}else{
         pCharBuffer = (char*)malloc(sizeof(char)*nNoOfCol*MAX_CHAR_ATTR_LEN) ;
	}

    SQLINTEGER   cbLen = 0 ;

    GetHandles(&stHandles, GET, 0) ;

    for(int c = 0 ; c < nTables ; ++c){

        int nCol = 0, nRows = 0 ;

        printf("Table: \"%s\":\n------------------\n", (char*)(szTableName + MAX_TABLE_NAME*c*sizeof(char))) ;

        sprintf((char*)szStmt, "SELECT * FROM %s", (char*)(szTableName + MAX_TABLE_NAME*c*sizeof(char))) ;
		if(nPrint) printf("\n> %s\n", szStmt) ;
        ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmt, SQL_NTS), retcode) ;

        for(int i = 0 ; i < nNoOfCol ; ++i){

            if(T_CHAR != attrType){
                ODBC_FN(SQLBindCol(stHandles.hstmt, (i+1), SQL_C_DOUBLE, (void*)&pDoubleBuffer[i], sizeof(SQLDOUBLE), &cbLen), retcode) ;
			}else{
                ODBC_FN(SQLBindCol(stHandles.hstmt, (i+1), SQL_C_CHAR, (void*)(pCharBuffer + i*MAX_CHAR_ATTR_LEN*sizeof(char)), MAX_CHAR_ATTR_LEN*sizeof(char), &cbLen), retcode) ;
			}
            nCol++ ;

		}

    int k = 0 ;	//out of the <for> loop
    for (;;) {

        retcode = SQLFetch(stHandles.hstmt);			
        if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ){
            for(k = 0 ; k < nNoOfCol ; ++k){
                if(T_CHAR != attrType){
                    ATTR_TYPE_SWITCH_T(pDoubleBuffer[k], AttributeType) ;
                    }else{
                        printf("%s\t", (char*)(pCharBuffer + k*MAX_CHAR_ATTR_LEN)) ;
					}
                }
            }else if(SQL_NO_DATA == retcode){
                if(0 == k){
                    printf("<empty>\n") ;
                    break ;
                    }else{
                        break ;
					}
                }else{
                    HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
				}

            ++nRows ;
            printf("\n") ;
        }

	SQLCloseCursor(stHandles.hstmt) ;

	printf("------------------\n") ;
	printf("Rows: %d Columns: %d\n\n", nRows, nCol) ;

			}

	free((void*)pDoubleBuffer) ;
	free((void*)pCharBuffer) ;
	GetHandles(&stHandles, FREE, 0) ;

	return ;
}



/*************************************************
Function: AssignRefCharValues()
*************************************************/
void AssignRefCharValues(char* pRef, bool bVerbose) {

    int count = 0, rows = 0, nThreadOffset = 0, nRowOffset = 0 ;
    char szStrBuffer[MAX_CHAR_ATTR_LEN] = { 0 } ;
    int char_count = sizeof(szANSI)/sizeof(char) ;

    for(int c = 0 ; c < nNoOfThreads ; ++c){
        nThreadOffset = nNoOfRows*nNoOfCol*c*MAX_CHAR_ATTR_LEN*sizeof(char) ;
        for(int d = 0 ; d < nNoOfRows ; ++d){
            nRowOffset = nNoOfCol*d*MAX_CHAR_ATTR_LEN*sizeof(char) ; ++rows ;
            for(int i = 0 ; i < nNoOfCol ; ++i){
                for(int j = 0 ; j < (MAX_CHAR_ATTR_LEN - 2) ; ++j){
                    int h = (char)(rand() % (char_count-1)) ;
                    szStrBuffer[j] = szANSI[h] ;
                    }
            szStrBuffer[MAX_CHAR_ATTR_LEN - 1] = '\0' ;

            strcpy((char*)(pRef + nThreadOffset + nRowOffset + i*MAX_CHAR_ATTR_LEN*sizeof(char)), (char*)szStrBuffer) ;
            count++ ;
            if(bVerbose){
                printf(" %s ", (char*)(pRef + nThreadOffset + nRowOffset + i*MAX_CHAR_ATTR_LEN*sizeof(char))) ;
                }
                }
        if(bVerbose) { 
            printf("\n") ;
            NdbSleep_MilliSleep(10) ;
            }
            }
        }

if(bVerbose){
    printf("_____________________") ;
    printf("\nRows: %d Values: %d\n\n", rows, count) ;
    }

return ;
    }


/*


sprintf((char*)szStmtBuffer, "INSERT INTO %s VALUES(", p->szTableName) ; 				
for(j = 0 ;;){
strcat((char*)szStmtBuffer, "?") ;
++j ;
if(nNoOfCol == j) break ;
strcat((char*)szStmtBuffer, ", ") ;
}
strcat((char*)szStmtBuffer, ")") ;

ODBC_FN(SQLPrepare(stHandles.hstmt, szStmtBuffer, SQL_NTS), retcode) ;

for(j = 0 ; j < nNoOfCol ; ++j){
ODBC_FN(SQLBindParameter(stHandles.hstmt, (j+1), SQL_PARAM_INPUT, SQL_C_FLOAT, SQL_FLOAT, 0, 0, (void*)&pBindBuffer[j], 0, &cbFloat), retcode) ;
HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
}

for(r = 0 ; r < nNoOfRows ; ++r){
for(j = 0 ; j < nNoOfCol ; ++j){
pBindBuffer[j] = pRef[nNoOfCol*r + j] ;
}
ODBC_FN(SQLExecute(stHandles.hstmt), retcode) ;
HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
}

*/




/*************************************************
Function: HandleError
*************************************************/

void HandleError(void* handle, SQLSMALLINT HandleType){

    SQLCHAR szError[MAX_STR_LEN], szSqlState[32] ;
    SQLINTEGER nError = 0 ;
    SQLSMALLINT nHandleType = HandleType ;
    SQLSMALLINT nLength = 0 ;
    SQLHANDLE SQLHandle = handle ;
    SQLGetDiagRec(nHandleType, SQLHandle, 1, szSqlState, &nError, szError, 128, &nLength) ;
    printf("Error: %s\nSqlState: %s\n", szError, szSqlState) ;

    return ;
    }



/*************************************************
Function: ReportError
*************************************************/

void ReportError(char* szFn, char* szBuffer, char* szFile, int iLine){

    printf("%s %s\nFile: %s\nLine: %d\n", szFn, szBuffer, szFile, iLine) ;

    return ;
}



/*************************************************
Function: GetHandles()
*************************************************/

SQLRETURN GetHandles(ODBC_HANDLES* pHandles, handle_op op, bool bDriverInfo){

    SQLRETURN   retcode = SQL_ERROR ;

    if(GET == op){
		
		retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &pHandles->henv);

        if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ) {
			retcode = SQLSetEnvAttr(pHandles->henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC2, 0);

            if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode) {
				retcode = SQLAllocHandle(SQL_HANDLE_DBC, pHandles->henv, &pHandles->hdbc);

                if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode) {
                    
					//SQLSetConnectAttr(pHandles->hdbc, SQL_LOGIN_TIMEOUT, (void*)5, 0);
					
					retcode = SQLConnect(pHandles->hdbc, (SQLCHAR*)"", SQL_NTS, (SQLCHAR*)"", SQL_NTS, (SQLCHAR*)"", SQL_NTS ) ;
					
					SQL_SUCCESS == SQLSetConnectAttr(pHandles->hdbc, SQL_ATTR_AUTOCOMMIT, (void*)SQL_AUTOCOMMIT_ON, 0) ;
						//printf("AUTOCOMMIT is on\n") ; 
					
					if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode)	{
						// retcode still holds the value returned by SQLConnect
					    retcode = SQLAllocHandle(SQL_HANDLE_STMT, pHandles->hdbc, &pHandles->hstmt) ;
						if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode) {
							if(bDriverInfo) GetDriverAndSourceInfo(pHandles->hdbc) ;
							// printf("All handles allocated OK\n", retcode);
						}else{ // SQLAllocHandle()
							REPORTERROR((char*)"SQLAllocHandle()", (char*)"failed") ;
							HandleError(pHandles->hdbc, SQL_HANDLE_DBC) ;
							ODBC_FN(SQLDisconnect(pHandles->hdbc), retcode) ;
							ODBC_FN(SQLFreeHandle(SQL_HANDLE_DBC, pHandles->hdbc), retcode) ;
							ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
							retcode = SQL_ERROR ;
						}
					}else{ // SQLConnect()
						REPORTERROR((char*)"SQLConnect()", (char*)"failed" ) ;
						HandleError(pHandles->hdbc, SQL_HANDLE_DBC) ;
						ODBC_FN(SQLFreeHandle(SQL_HANDLE_DBC, pHandles->hdbc), retcode) ;
						ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
						retcode = SQL_ERROR ;
					}
				}else{ // SQLAllocHandle()
					REPORTERROR((char*)"SQLAllocHandle()", "failed" ) ;
					HandleError(pHandles->hdbc, SQL_HANDLE_DBC) ;
					ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
					retcode = SQL_ERROR ;
				}
			}else{ // SQLSetEnvAttr()
				REPORTERROR((char*)"SQLSetEnvAttr()", "failed" ) ;
				HandleError(pHandles->henv, SQL_HANDLE_ENV) ;
				ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
				retcode = SQL_ERROR ;
			}
		}else{ // SQLAllocHandle()
			REPORTERROR((char*)"SQLAllocHandle()", "failed" ) ;
			HandleError(pHandles->henv, SQL_HANDLE_ENV) ;
			retcode = SQL_ERROR ;
		}
	}else{
			ODBC_FN(SQLFreeHandle(SQL_HANDLE_STMT, pHandles->hstmt), retcode) ;
			ODBC_FN(SQLDisconnect(pHandles->hdbc), retcode) ;
			ODBC_FN(SQLFreeHandle(SQL_HANDLE_DBC, pHandles->hdbc), retcode) ;
			ODBC_FN(SQLFreeHandle(SQL_HANDLE_ENV, pHandles->henv), retcode) ;
	}

    return retcode ;
}



/*************************************************
Function: AggretateFn():
<aggr_fn fn> - name of the aggregate function to use
<char* szTableName> - name of the table
<int nCol> - number of the column
<double* pdIn> - pointer to double containing the value to be used in a call to COUNT; used only by this function
<double* pdOut> - pointer to double that will recieve the result
<attr_type attrType> - type of the attribute
*************************************************/
SQLRETURN AggregateFn(aggr_fn fn, char* szTableName, int nCol, double* pdIn, double* pdOut, attr_type attrType){

    SQLRETURN retcode = SQL_ERROR ;
    SQLCHAR* szStmt[MAX_SQL_STMT] = { 0 } ;
    SQLCHAR szValueBuffer[MAX_VALUE_LEN] = { 0 } ;
    SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
    SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;
    SQLINTEGER  cbDouble = 0 ;

    GetHandles(&stHandles, GET, 0) ;

    switch(fn){
    case FN_COUNT:
        switch(attrType){
    case T_INT:
        sprintf((char*)szStmt, "SELECT COUNT(*) FROM %s WHERE COL%d > %d", szTableName, nCol, (int)*pdIn) ;
        break ;
    case T_FLOAT:
        sprintf((char*)szStmt, "SELECT COUNT(*) FROM %s WHERE COL%d > %f", szTableName, nCol, (float)*pdIn) ;
        break ;
/*    case T_DOUBLE:
        sprintf((char*)szStmt, "SELECT COUNT(*) FROM %s WHERE COL%d > %.15f", szTableName, nCol, *pdIn) ;
        break ;
*/
    default:
        break ;
            }
    break ;
    case FN_SUM:
        sprintf((char*)szStmt, "SELECT SUM(COL%d) FROM %s", nCol, szTableName) ;
        break ;
    case FN_AVG:
        sprintf((char*)szStmt, "SELECT AVG(COL%d) FROM %s", nCol, szTableName) ;
        break ;
    case FN_MAX:
        sprintf((char*)szStmt, "SELECT MAX(COL%d) FROM %s", nCol, szTableName) ;
        break ;
    case FN_MIN:
        sprintf((char*)szStmt, "SELECT MIN(COL%d) FROM %s", nCol, szTableName) ;
        break ;
    case FN_VARIANCE: // not implemented
        //sprintf((char*)szStmt, "SELECT VARIANCE(COL%d) FROM %s;", nCol, szTableName) ;
        break ;
    case FN_STDDEV: // not implemented
        //sprintf((char*)szStmt, "SELECT STDDEV(COL%d) FROM %s;", nCol, szTableName) ;
        break ;
    default:
        break ;
        }
//printf("%s\n", szStmt) ; 

retcode = SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmt, SQL_NTS) ;
if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ){
    retcode = SQLBindCol(stHandles.hstmt, 1, SQL_C_DOUBLE, (void*)pdOut, sizeof(SQLDOUBLE), &cbDouble) ;
    if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode ){
        retcode = SQLFetch(stHandles.hstmt) ;
        }
    }

if(SQL_SUCCESS != retcode && SQL_SUCCESS_WITH_INFO != retcode){
    HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
    }

SQLCloseCursor(stHandles.hstmt) ;

GetHandles(&stHandles, FREE, 0) ;

return retcode ;

    };



/*************************************************
Function: GetDriverAndSourceInfo()
*************************************************/
SQLRETURN GetDriverAndSourceInfo(SQLHDBC hdbc){

    SQLRETURN retcode = SQL_ERROR ;

    SQLCHAR			buffer[255] ;
    SQLUSMALLINT	snValue = 0 ;
    SQLSMALLINT		outlen =  0 ;

    printf( "-------------------------------------------\n" ) ;

    retcode = SQLGetInfo( hdbc, SQL_DATA_SOURCE_NAME, buffer, 255, &outlen ) ;

    printf( "Connected to Server: %s\n", buffer ) ;

    retcode = SQLGetInfo( hdbc, SQL_DATABASE_NAME, buffer, 255, &outlen ) ;
    printf( "      Database name: %s\n", buffer ) ;

    retcode = SQLGetInfo( hdbc, SQL_SERVER_NAME, buffer, 255, &outlen ) ;
    printf( "      Instance name: %s\n", buffer ) ;

    retcode = SQLGetInfo( hdbc, SQL_DBMS_NAME, buffer, 255, &outlen ) ;
    printf( "          DBMS name: %s\n", buffer ) ;

    retcode = SQLGetInfo( hdbc, SQL_DBMS_VER, buffer, 255, &outlen ) ;
    printf( "       DBMS version: %s\n", buffer ) ;

    retcode = SQLGetInfo( hdbc, SQL_ODBC_VER, buffer, 255, &outlen ) ;
    printf( "       ODBC version: %s\n", buffer ) ;

    retcode = SQLGetInfo( hdbc, SQL_DRIVER_NAME, buffer, 255, &outlen ) ;
    printf( "        Driver name: %s\n", buffer ) ;

    retcode = SQLGetInfo( hdbc, SQL_DRIVER_VER, buffer, 255, &outlen ) ;
    printf( "     Driver version: %s\n", buffer ) ;

    retcode = SQLGetInfo( hdbc, SQL_MAX_DRIVER_CONNECTIONS, &snValue, sizeof(SQLSMALLINT), &outlen ) ;
    printf( "    Max connections: %d\n", snValue ) ;

    retcode = SQLGetInfo( hdbc, SQL_CURSOR_COMMIT_BEHAVIOR, &snValue, sizeof(SQLSMALLINT), &outlen ) ;
    printf( "Autocommit behavior:") ;

    switch(snValue){
    case SQL_CB_DELETE:
        printf(" SQL_CB_DELETE\n") ;
        break ;
    case SQL_CB_CLOSE:
        printf(" SQL_CB_CLOSE\n") ;
        break ;
    case SQL_CB_PRESERVE:
        printf(" SQL_CB_PRESERVE\n") ;
        break ;
    default:
        printf(" undefined\n") ;
        break ;
        }

	printf( "-------------------------------------------\n" ) ;

	return retcode ;

}



/*************************************************
Function: ArithOp()
*************************************************/

int ArithOp(char* szTable, int nTotalCols, float* pValue, attr_type attrType, arth_op op){
	
    SQLRETURN retcode = SQL_ERROR ;
	int nVerRet = -1 ;
    SQLCHAR szStmt[MAX_SQL_STMT] = { 0 } ;
    SQLCHAR szEndBuffer[MAX_STR_LEN] = { 0 } ;
    SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
    SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;
    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;

    void* pBuffer = NULL ;
    SQLINTEGER BindInt = 0, IntResult = 0, RefIntResult = 0 ;
    SQLFLOAT BindFloat = 0, FloatResult = 0, RefFloatResult = 0 ;
    SQLDOUBLE BindDouble = 0, DoubleResult = 0, RefDoubleResult = 0 ;
    SQLINTEGER cbSize = 0 ;
    SQLINTEGER  cbLen = 0 ;
    SQLSMALLINT cbTarget = 0 ;

    GetHandles(&stHandles, GET, 0) ;

    for(int c = 0 ; c < nTotalCols ; ++c){

		sprintf((char*)szStmt, "SELECT COL%d, (COL%d", c, c) ;
        switch(op){
        case MINUS:
            strcat((char*)szStmt, " - ") ;
            break ;
        case PLUS:
            strcat((char*)szStmt, " + ") ;
            break ;
        case MULTI:
            strcat((char*)szStmt, " * ") ;
            break ;
        case DIVIDE:
            strcat((char*)szStmt, " / ") ;
            break ;
        case MODULO:
            //strcat((char*)szStmt, " % ") ; Not implemented
            GetHandles(&stHandles, FREE, 0) ;
            return -1 ; //Close handles and return
            break ;
        default:
            break ;
		}

		sprintf((char*)(szAuxBuffer),"%.9f) ", *((float*)(pValue))) ;
		strcat((char*)szStmt, (char*)szAuxBuffer) ;
		sprintf((char*)szEndBuffer, "FROM %s", szTable) ;
		strcat((char*)szStmt, (char*)szEndBuffer) ;

		ODBC_FN(SQLExecDirect(stHandles.hstmt, (SQLCHAR*)szStmt, SQL_NTS), retcode) ;
		if(retcode == SQL_ERROR){
			HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ; 
			printf("\n%s\n", szStmt) ;
		}

		SQLSMALLINT cbNameLen = 0, cbSqlType = 0, cbNullable = 0, cbColScale = 0  ;
		SQLINTEGER cbColSize = 0 ;
		SQLDescribeCol(stHandles.hstmt, 2, szColBuffer, MAX_COL_NAME-1, &cbNameLen, &cbSqlType, (unsigned long*)&cbColSize, &cbColScale, &cbNullable) ;

		switch(cbSqlType){
			case SQL_NUMERIC:
				pBuffer = &IntResult ;
				cbSize = sizeof(SQLINTEGER) ;
				cbTarget = SQL_C_ULONG ;
			case SQL_INTEGER:
				pBuffer = &IntResult ;
				cbSize = sizeof(SQLINTEGER) ;
				cbTarget = SQL_C_LONG ;
				break ;
			case SQL_FLOAT:
				pBuffer = &FloatResult ; 
				cbSize = sizeof(SQLFLOAT) ;
				cbTarget = SQL_C_FLOAT ;
				break ;
			case SQL_DOUBLE:
				pBuffer = &DoubleResult ;
				cbSize = sizeof(SQLDOUBLE) ;
				cbTarget = SQL_C_DOUBLE ;
				break ;
			default:
				printf("\nUndefined result type: %d\n", cbSqlType) ;
				break ;
		}

		switch(attrType){
			case T_INT:
				ODBC_FN(SQLBindCol(stHandles.hstmt, 1, SQL_C_SLONG, (void*)&BindInt, sizeof(SQLINTEGER), &cbLen), retcode) ;
				break ;
			case T_FLOAT:
				ODBC_FN(SQLBindCol(stHandles.hstmt, 1, SQL_C_FLOAT, (void*)&BindFloat, sizeof(SQLFLOAT), &cbLen), retcode) ;
				break ;
	/*        case T_DOUBLE:
				ODBC_FN(SQLBindCol(stHandles.hstmt, 1, SQL_C_DOUBLE, (void*)&BindDouble, sizeof(SQLDOUBLE), &cbLen), retcode) ;
				break ;
	*/
			default:
				break ;
		}

		ODBC_FN(SQLBindCol(stHandles.hstmt, 2, cbTarget, pBuffer, cbSize, &cbLen), retcode) ;

		retcode = SQLFetch(stHandles.hstmt) ;

		if (SQL_SUCCESS == retcode || SQL_SUCCESS_WITH_INFO == retcode){
			switch(attrType){
				case T_INT:
					switch(cbSqlType){
						case SQL_INTEGER:
							nVerRet = VerifyArthOp((int*)&BindInt, pValue, (int*)pBuffer, op) ;
							break ;
						case SQL_FLOAT:
							nVerRet = VerifyArthOp((int*)&BindInt, pValue, (float*)pBuffer, op) ;
							break ;
						case SQL_DOUBLE:
							nVerRet = VerifyArthOp((int*)&BindInt, pValue, (double*)pBuffer, op) ;
							break ;
						case SQL_NUMERIC:
							nVerRet = VerifyArthOp((int*)&BindInt, pValue, (int*)pBuffer, op) ;
							break ;
						default:
							break ;
					}
					break ;

				case T_FLOAT:
					switch(cbSqlType){
						case SQL_INTEGER:
							nVerRet = VerifyArthOp((float*)&BindFloat, pValue, (int*)pBuffer, op) ;
							break ;
						case SQL_FLOAT:
							nVerRet = VerifyArthOp((float*)&BindFloat, pValue, (float*)pBuffer, op) ;
							break ;
						case SQL_DOUBLE:
							nVerRet = VerifyArthOp((float*)&BindFloat, pValue, (double*)pBuffer, op) ;
							break ;
						default:
							break ;
					}
					break ;
	/*                case T_DOUBLE:
						switch(cbSqlType){
					case SQL_INTEGER:
						nVerRet = VerifyArthOp((double*)&BindDouble, pValue, (int*)pBuffer, op) ;
						break ;
					case SQL_FLOAT:
						nVerRet = VerifyArthOp((double*)&BindDouble, pValue, (float*)pBuffer, op) ;
						break ;
					case SQL_DOUBLE:
						nVerRet = VerifyArthOp((double*)&BindDouble, pValue, (double*)pBuffer, op) ;
						break ;
					default:
						break ;
							}
					break ;
	*/
				default:
					break ;
			}
			if(-1 == nVerRet){
				printf("\nVerification failed.\n") ;
				return nVerRet ;
			}else if(SQL_NO_DATA == retcode){
				break ;
			}
		}else{

			HandleError(stHandles.hstmt, SQL_HANDLE_STMT) ;
		}

		SQLCloseCursor(stHandles.hstmt) ;
		}
		
		GetHandles(&stHandles, FREE, 0) ;
		
		return nVerRet ;
}




/*************************************************
Function: Join()
*************************************************/
SQLRETURN Join(char* szTable, int nTables, int nCol, join_type joinType){

    SQLRETURN retcode = SQL_ERROR ;
    SQLCHAR szStmt[MAX_SQL_STMT] = { 0 } ;
    SQLCHAR szEndBuffer[MAX_STR_LEN] = { 0 } ;
    SQLCHAR szColBuffer[MAX_COL_NAME] = { 0 } ;
    SQLCHAR szAuxBuffer[MAX_STR_LEN] = { 0 } ;

    ODBC_HANDLES stHandles ;
    memset(&stHandles, 0, sizeof(ODBC_HANDLES)) ;

    int c = 0, t = 0 ;

    GetHandles(&stHandles, GET, 0) ;

    for(c = 0 ; c < nCol ; ++c) {

        switch(joinType){
        case ITSELF:
            sprintf((char*)szStmt, "SELECT * FROM %s, %s", (char*)szTable, (char*)szTable) ;
            break ;
        case EQUI:
            break ;
        case NON_EQUI:
            break ;
        case INNER:
            break ;
        case OUTTER:
            break ;
        default:
            break ;
            }
        }

GetHandles(&stHandles, FREE, 0) ;

return retcode ;

}



SQLRETURN GetResults(SQLHSTMT){

    SQLRETURN retcode = SQL_ERROR ;

    return retcode ;
}

/*

int createTables(char* szTableName, int nTables){
  
	for (int i = 0; i < nNoOfCol; i++){
		snprintf(attrName[i], MAXSTRLEN, "COL%d", i) ;
	}

	for (int i = 0; i < nTables; i++){
		snprintf(tableName[i], MAXSTRLEN, "TAB%d", i) ;
	}
  
	for(unsigned i = 0; i < nTables; i++){
		
		ndbout << "Creating " << szTableName[i] << "... " ;
		
		NDBT_Table tmpTable(szTableName[i]) ;
		
		tmpTable.setStoredTable(!theTempTable) ;
    
  		tmpTable.addAttribute(NDBT_Attribute(attrName[0],
                                    UnSigned,
                                    4, // 4 Bytes
                                    TupleKey));
	}

		
	for (int j = 1 ; j < nNoOfCol ; j++)
		tmpTable.addAttribute(NDBT_Attribute(attrName[j], UnSigned, 4*tAttributeSize)) ;

	if(tmpTable.createTableInDb(pMyNdb) == -1){
		return -1 ;
	}
    
	ndbout << "done" << endl ;
		
	return 0;
}
*/

/*************************************************
Function: createTables()
Uses NDB API to create tables for the tests
*************************************************/

int createTables(char* szTableName, int nTables){

  Ndb * pNdb = new Ndb("TEST_DB") ;
  pNdb->init();
  
  ndbout << "Waiting for ndb to become ready..." <<endl;
  if (pNdb->waitUntilReady(10000) != 0){
    ndbout << "NDB is not ready" << endl;
    ndbout << "Benchmark failed!" << endl;
    delete pNdb ;
	return -1 ;
  }

  NdbSchemaCon          *MySchemaTransaction = NULL ;
  NdbSchemaOp           *MySchemaOp = NULL ;
  int                   check = -1 ;
  char					szColNameBuffer[MAX_COL_NAME] = { 0 } ;
  int					tLoadFactor = 80 ;
  
  for(int i=0 ; i < nTables ; ++i) {
	  
	  ndbout << "Creating " << (char*)(szTableName+MAX_TABLE_NAME*i) << "..." << endl ;

      MySchemaTransaction = pNdb->startSchemaTransaction() ;
	  //printf("MySchemaTransaction - OK\n") ;
      if(MySchemaTransaction == NULL){
		  printf("MySchemaTransaction is NULL\n") ;
		  delete pNdb ;
		  return -1 ;
	  }
      
      MySchemaOp = MySchemaTransaction->getNdbSchemaOp();       
  	  //printf("MySchemaTransaction->getNdb... - OK\n") ;
      if(MySchemaOp == NULL){
  		  printf("MySchemaOp is NULL\n") ;
		  delete pNdb ;
		  return -1 ;
	  }
      
      check = MySchemaOp->createTable( (const char*)(szTableName+MAX_TABLE_NAME*i)
                                       ,8                       // Table Size
                                       ,TupleKey                // Key Type
                                       ,40                      // Nr of Pages
                                       ,All
                                       ,6
                                       ,(tLoadFactor - 5)
                                       ,(tLoadFactor)
                                       ,1
                                       ,0
                                       );
      
      if (check == -1){
 		  printf("MySchemaOp->createTable failed\n") ;
		  delete pNdb ;
		  return -1 ;
	  }

      snprintf(szColNameBuffer, MAX_COL_NAME, "COL%d", 0) ;
      check = MySchemaOp->createAttribute( szColNameBuffer,
                                           TupleKey,
                                           32,
                                           PKSIZE,
                                           UnSigned,
                                           MMBased,
                                           NotNullAttribute );
      
      if (check == -1){
  		  printf("MySchemaOp->createAttribute() #1 failed\n") ;
		  delete pNdb ;
		  return -1 ;
	  }

      for (int j = 1; j < nNoOfCol ; j++){
		  snprintf(szColNameBuffer, MAX_COL_NAME, "COL%d", j) ;
		  check = MySchemaOp->createAttribute(szColNameBuffer,
                                             NoKey,
                                             32,
                                             tAttributeSize,
                                             UnSigned,
                                             MMBased,
                                             NotNullAttribute );
		
		  if (check == -1){
    		  printf("MySchemaOp->createAttribute() #2 failed\n") ;
			  delete pNdb ;
			  return -1;
		  }
      }
      
      if (MySchemaTransaction->execute() == -1){
   		  printf("MySchemaTransaction->execute() failed\n") ;
		  printf("%s\n", MySchemaTransaction->getNdbError().message) ; 
		  return -1 ;
		  delete pNdb ;
	  }
      
      pNdb->closeSchemaTransaction(MySchemaTransaction);
    }
  
  return 0;
}



