#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>

#define MAX_STRING_LEN	2048
#define TIMEOUT		10

static int	error(SQLSMALLINT h_type, SQLHANDLE h, SQLRETURN rc)
{
	if (SQL_ERROR == rc || SQL_SUCCESS_WITH_INFO == rc)
	{
		SQLCHAR		sql_state[SQL_SQLSTATE_SIZE + 1], err_msg[MAX_STRING_LEN];
		SQLINTEGER	err_code = 0;
		SQLSMALLINT	rec_nr = 1;

		while (SQL_SUCCEEDED(SQLGetDiagRec(h_type, h, rec_nr++, sql_state, &err_code, err_msg, sizeof(err_msg), NULL)))
			printf("[%s][%ld][%s]\n", sql_state, (long)err_code, err_msg);
	}

	return !SQL_SUCCEEDED(rc);
}

int	main(void)
{
	char		*dsn, *user, *pass, *query, buffer[MAX_STRING_LEN + 1];
	SQLHENV		env;
	SQLHDBC		dbc;
	SQLHSTMT	stmt;
	SQLSMALLINT	col_num;
	SQLLEN		col_type;
	SQLLEN		len;
	SQLRETURN	rc;

	printf("DSN:\n");
	if (1 != scanf("%ms", &dsn))
	{
		printf("Invalid input!\n");
		exit(EXIT_FAILURE);
	}

	printf("Username:\n");
	if (1 != scanf("%ms", &user))
	{
		printf("Invalid input!\n");
		exit(EXIT_FAILURE);
	}

	printf("Pasword:\n");
	if (1 != scanf("%ms", &pass))
	{
		printf("Invalid input!\n");
		exit(EXIT_FAILURE);
	}

	printf("Query:\n");
	if (1 != scanf("\n%m[^\n]%*c", &query))
	{
		printf("Invalid input!\n");
		exit(EXIT_FAILURE);
	}

	if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env)))
	{
		printf("Cannot create ODBC environment handle.\n");
		exit(EXIT_FAILURE);
	}

	if (error(SQL_HANDLE_ENV, env, SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0)))
	{
		printf("Cannot set ODBC version!\n");
		exit(EXIT_FAILURE);
	}

	if(error(SQL_HANDLE_ENV, env, SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc)))
	{
		printf("Cannot create ODBC connection handle!\n");
		exit(EXIT_FAILURE);
	}

	if (error(SQL_HANDLE_DBC, dbc, SQLSetConnectAttr(dbc, (SQLINTEGER)SQL_LOGIN_TIMEOUT, (SQLPOINTER)(intptr_t)TIMEOUT, (SQLINTEGER)0)))
	{
		printf("Cannot set ODBC login timeout!\n");
		exit(EXIT_FAILURE);
	}

	if (error(SQL_HANDLE_DBC, dbc, SQLConnect(dbc, (SQLCHAR *)dsn, SQL_NTS, (SQLCHAR *)user, SQL_NTS, (SQLCHAR *)pass, SQL_NTS)))
	{
		printf("Cannot connect to ODBC DSN!\n");
		exit(EXIT_FAILURE);
	}

	if (error(SQL_HANDLE_DBC, dbc, SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt)))
	{
		printf("Cannot create ODBC statement handle!\n");
		exit(EXIT_FAILURE);
	}

	if (error(SQL_HANDLE_STMT, stmt, SQLExecDirect(stmt, (SQLCHAR *)query, SQL_NTS)))
	{
		printf("Cannot execute ODBC query!\n");
		exit(EXIT_FAILURE);
	}

	if (error(SQL_HANDLE_STMT, stmt, SQLNumResultCols(stmt, &col_num)))
	{
		printf("Cannot get number of columns in ODBC result!\n");
		exit(EXIT_FAILURE);
	}

	printf("column number:%d\n", (int)col_num);

	/* from now on assuming one column and one row */

	if (error(SQL_HANDLE_STMT, stmt, SQLColAttribute(stmt, 1, SQL_DESC_TYPE, NULL, 0, NULL, &col_type)))
	{
		printf("Cannot get column type!\n");
		exit(EXIT_FAILURE);
	}

	/* force col_type to integer value for DB2 compatibility */
	col_type = (SQL_WLONGVARCHAR == (int)col_type ? SQL_C_DEFAULT : SQL_C_CHAR);
	printf("column type:%d\n", (int)col_type);

	if (error(SQL_HANDLE_STMT, stmt, SQLFetch(stmt)))
	{
		printf("Cannot fetch row!\n");
		exit(EXIT_FAILURE);
	}

	do
	{
		rc = SQLGetData(stmt, 1, col_type, buffer, MAX_STRING_LEN, &len);

		if (error(SQL_HANDLE_STMT, stmt, rc))
		{
			printf("Cannot get column data!\n");
			exit(EXIT_FAILURE);
		}

		/* force len to integer value for DB2 compatibility */
		if (SQL_NULL_DATA == (int)len)
			break;

		printf("len:%d MAX_STRING_LEN:%d buffer:'%*s'\n", (int)len, MAX_STRING_LEN, (int)len, buffer);
	}
	while (SQL_SUCCESS != rc);

	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
	SQLDisconnect(dbc);
	SQLFreeHandle(SQL_HANDLE_DBC, dbc);
	SQLFreeHandle(SQL_HANDLE_ENV, env);

	free(query);
	free(pass);
	free(user);
	free(dsn);

	exit(EXIT_SUCCESS);
}
