/*
*	©Copyright 2002,2003, Microsoft Corporation
*	Microsoft Confidential.
*	Written by Sergey Vasilevskiy, 2002-2003.
*/

/*
*	Customer Position database class that uses stored procedures for individual frames.
*/

#include "stdafx.h"

using namespace TPCE;

//Constructor
CCustomerPositionDB::CCustomerPositionDB(CDBConnection *pDBConn)
: CTxnHarnessDBBase(pDBConn)
{
	int i;	

	//Allocate one handle per stored procedure (frame)
	if (   SQLAllocHandle(SQL_HANDLE_STMT, m_hdbc, &m_hstmtFrame1) != SQL_SUCCESS
		|| SQLAllocHandle(SQL_HANDLE_STMT, m_hdbc, &m_hstmtFrame2) != SQL_SUCCESS
		|| SQLAllocHandle(SQL_HANDLE_DESC, m_hdbc, &m_descFrame1Col1) != SQL_SUCCESS
		|| SQLAllocHandle(SQL_HANDLE_DESC, m_hdbc, &m_descFrame1Col2) != SQL_SUCCESS
		)
		ThrowError(CODBCERR::eAllocHandle, SQL_HANDLE_STMT, m_hdbc);
	
	//Set statement descriptor for the parameters and the first rowset
	if ( SQLSetStmtAttrW( m_hstmtFrame1, SQL_ATTR_APP_ROW_DESC, m_descFrame1Col1, SQL_IS_POINTER ) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1);

	//Bind input/output parameters for each stored procedure

	//Frame1 input
	i = 0;
	if (   SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_IDENT, SQL_IDENT, 0, 0, &Frame1Input.cust_id, 0, NULL)!=SQL_SUCCESS
		|| SQLBindParameter(m_hstmtFrame1, ++i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, cTAX_ID_len+1, 0, Frame1Input.tax_id, cTAX_ID_len+1, NULL)!=SQL_SUCCESS
		 )
		ThrowError(CODBCERR::eBindParam, SQL_HANDLE_STMT, m_hstmtFrame1);
	//Frame1 output	

	//These columns are associated with the first descriptor
	i = 0;
	if (   SQLBindCol(m_hstmtFrame1, ++i, SQL_C_IDENT, &Frame1Output.cust_id, 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_st_id, cST_ID_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_l_name, cL_NAME_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_f_name, cF_NAME_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_m_name, cM_NAME_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_gndr, cGNDR_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_STINYINT, &Frame1Output.c_tier, 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_TYPE_TIMESTAMP, &Frame1Output.c_dob, 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_IDENT, &Frame1Output.c_ad_id, 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_ctry_1, cCTRY_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_area_1, cAREA_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_local_1, cLOCAL_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_ext_1, cEXT_len+1, &m_c_ext_1_ind) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_ctry_2, cCTRY_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_area_2, cAREA_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_local_2, cLOCAL_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_ext_2, cEXT_len+1, &m_c_ext_2_ind) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_ctry_3, cCTRY_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_area_3, cAREA_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_local_3, cLOCAL_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_ext_3, cEXT_len+1, &m_c_ext_3_ind) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_email_1, cEMAIL_len+1, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_CHAR, Frame1Output.c_email_2, cEMAIL_len+1, NULL) != SQL_SUCCESS
		)
		ThrowError(CODBCERR::eBindCol, SQL_HANDLE_STMT, m_hstmtFrame1);

	//Set statement descriptor for the second rowset
	if ( SQLSetStmtAttrW( m_hstmtFrame1, SQL_ATTR_APP_ROW_DESC, m_descFrame1Col2, SQL_IS_POINTER ) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1);

	SQLSetStmtAttr(m_hstmtFrame1, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);	//set column-wise binding although it is the default
	SQLSetStmtAttr(m_hstmtFrame1, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)(sizeof(Frame1Output.acct_id)/sizeof(Frame1Output.acct_id[0])), SQL_IS_INTEGER);
	//SQLSetStmtAttr(m_hstmtFrame1, SQL_ATTR_ROW_STATUS_PTR, StatusArray, 0);
	SQLSetStmtAttr(m_hstmtFrame1, SQL_ATTR_ROWS_FETCHED_PTR, &Frame1Output.acct_len, SQL_IS_POINTER);

	i = 0;
	if (   SQLBindCol(m_hstmtFrame1, ++i, SQL_C_IDENT, &Frame1Output.acct_id[0], 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_DOUBLE, &Frame1Output.cash_bal[0], 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame1, ++i, SQL_C_DOUBLE, &Frame1Output.asset_total[0], 0, NULL) != SQL_SUCCESS
		)
		ThrowError(CODBCERR::eBindCol, SQL_HANDLE_STMT, m_hstmtFrame1);
	

	//Frame2 input
	i = 0;
	if (   //SQLBindParameter(m_hstmtFrame2, ++i, SQL_PARAM_OUTPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &Frame2Output.status, 0, NULL)!=SQL_SUCCESS //sp return code
		SQLBindParameter(m_hstmtFrame2, ++i, SQL_PARAM_INPUT, SQL_C_IDENT, SQL_IDENT, 0, 0, &Frame2Input.acct_id, 0, NULL)!=SQL_SUCCESS		
		 )
		ThrowError(CODBCERR::eBindParam, SQL_HANDLE_STMT, m_hstmtFrame2);
	//Frame2 output
	SQLSetStmtAttr(m_hstmtFrame2, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0);
	SQLSetStmtAttr(m_hstmtFrame2, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)(sizeof(Frame2Output.trade_id)/sizeof(Frame2Output.trade_id[0])), SQL_IS_INTEGER);
	//SQLSetStmtAttr(m_hstmtFrame2, SQL_ATTR_ROW_STATUS_PTR, StatusArray, 0);
	SQLSetStmtAttr(m_hstmtFrame2, SQL_ATTR_ROWS_FETCHED_PTR, &Frame2Output.hist_len, SQL_IS_POINTER);

	i = 0;
	if (   SQLBindCol(m_hstmtFrame2, ++i, SQL_C_TYPE_TIMESTAMP, &Frame2Output.hist_dts[0], 0, NULL) != SQL_SUCCESS		
		|| SQLBindCol(m_hstmtFrame2, ++i, SQL_C_SLONG, &Frame2Output.qty[0], 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame2, ++i, SQL_C_CHAR, &Frame2Output.symbol[0], sizeof(Frame2Output.symbol[0]), NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame2, ++i, SQL_C_IDENT, &Frame2Output.trade_id[0], 0, NULL) != SQL_SUCCESS
		|| SQLBindCol(m_hstmtFrame2, ++i, SQL_C_CHAR, &Frame2Output.trade_status[0], sizeof(Frame2Output.trade_status[0]), NULL) != SQL_SUCCESS
		)
		ThrowError(CODBCERR::eBindCol, SQL_HANDLE_STMT, m_hstmtFrame2);
	
};

//Destructor
CCustomerPositionDB::~CCustomerPositionDB()
{
	SQLFreeHandle(SQL_HANDLE_STMT, m_hstmtFrame1);
	SQLFreeHandle(SQL_HANDLE_STMT, m_hstmtFrame2);
	SQLFreeHandle(SQL_HANDLE_DESC, m_descFrame1Col1);
	SQLFreeHandle(SQL_HANDLE_DESC, m_descFrame1Col2);
}

void CCustomerPositionDB::DoCustomerPositionFrame1(const TCustomerPositionFrame1Input	*pFrame1Input, PCustomerPositionFrame1Output	pFrame1Output)
{
	int rc;

	//Set statement descriptor for the parameters and the first rowset
	if ( SQLSetStmtAttrW( m_hstmtFrame1, SQL_ATTR_APP_ROW_DESC, m_descFrame1Col1, SQL_IS_POINTER ) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1, "DoCustomerPositionFrame1");

	//copy input parameters into bound locations
	Frame1Input = *pFrame1Input;

	rc = SQLExecDirect(m_hstmtFrame1, (SQLCHAR*)"{call CustomerPositionFrame1(?, ?)}", SQL_NTS);
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		ThrowError(CODBCERR::eExecDirect, SQL_HANDLE_STMT, m_hstmtFrame1, "DoCustomerPositionFrame1");

	//2 sets => fetch the first set here (the proper descriptor is already associated with the statement)
	if ( SQLFetch(m_hstmtFrame1) == SQL_ERROR)
		ThrowError(CODBCERR::eFetch, SQL_HANDLE_STMT, m_hstmtFrame1, "DoCustomerPositionFrame1");

    // Check returned NULL values.
    //
    if (m_c_ext_1_ind == SQL_NULL_DATA)
    {
        Frame1Output.c_ext_1[0] = '\0';
    }
    if (m_c_ext_2_ind == SQL_NULL_DATA)
    {
        Frame1Output.c_ext_2[0] = '\0';
    }
    if (m_c_ext_3_ind == SQL_NULL_DATA)
    {
        Frame1Output.c_ext_3[0] = '\0';
    }

	//move on to the second result set
	if ( SQLMoreResults(m_hstmtFrame1) == SQL_ERROR)
		ThrowError(CODBCERR::eMoreResults, SQL_HANDLE_STMT, m_hstmtFrame1, "DoCustomerPositionFrame1");

	//Set statement descriptor for the second rowset
	if ( SQLSetStmtAttrW( m_hstmtFrame1, SQL_ATTR_APP_ROW_DESC, m_descFrame1Col2, SQL_IS_POINTER ) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame1, "DoCustomerPositionFrame1");

	// Initialize acct_len to 0 as there can be no rows returned and then
	// the list_len will not be initialized by SQLFetch().
	//
	Frame1Output.acct_len = 0;

	if ( SQLFetch(m_hstmtFrame1) == SQL_ERROR)
		ThrowError(CODBCERR::eFetch, SQL_HANDLE_STMT, m_hstmtFrame1, "DoCustomerPositionFrame1");

	//copy output parameters back
	*pFrame1Output = Frame1Output;	//invoke default assignment operator

	// Need to find out here if there are more rows than maximum allowed.
	// Attempt to fetch more. This should normally fail with SQL_NO_DATA.
	//
	rc = SQLFetch(m_hstmtFrame1);

	if (rc != SQL_NO_DATA && rc != SQL_ERROR)
	{	// Set to max allowed value + 1. The harness will return appropriate status code.
		//
		pFrame1Output->acct_len = max_acct_len + 1;
	}

	//if ((rc = SQLCloseCursor(m_hstmtFrame1)) == SQL_ERROR)
	//	ThrowError(CODBCERR::eCloseCursor, SQL_HANDLE_STMT, m_hstmtFrame1, "DoCustomerPositionFrame1");

	// Need two SQLFreeStmt calls for some reason to clean the statement
	// handle properly after the server sent "Transaction count incorrect after batch" message
	// with the result set.
	//
	rc = SQLFreeStmt(m_hstmtFrame1, SQL_CLOSE);
	rc = SQLFreeStmt(m_hstmtFrame1, SQL_CLOSE);
};

void CCustomerPositionDB::DoCustomerPositionFrame2(const TCustomerPositionFrame2Input	*pFrame2Input, PCustomerPositionFrame2Output	pFrame2Output)
{
	int rc;

	//copy input parameters into bound locations
	Frame2Input.acct_id = pFrame2Input->acct_id;
	
	//	Configure block cursor to avoid sp_cursoropen
	//
	if ( SQLSetStmtAttrW(m_hstmtFrame2, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame2, "DoCustomerPositionFrame2");

	rc = SQLExecDirect(m_hstmtFrame2, (SQLCHAR*)"{call CustomerPositionFrame2(?)}", SQL_NTS);
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
		ThrowError(CODBCERR::eExecDirect, SQL_HANDLE_STMT, m_hstmtFrame2, "DoCustomerPositionFrame2");

	//	Configure block cursor back to receive all the rows
	//
	if ( SQLSetStmtAttrW(m_hstmtFrame2, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)max_hist_len, 0) != SQL_SUCCESS )
		ThrowError(CODBCERR::eSetStmtAttr, SQL_HANDLE_STMT, m_hstmtFrame2, "DoCustomerPositionFrame2");

	// Initialize hist_len to 0 as there can be no rows returned and then
	// the list_len will not be initialized by SQLFetch().
	//
	Frame2Output.hist_len = 0;

	if ( SQLFetch(m_hstmtFrame2) == SQL_ERROR)
		ThrowError(CODBCERR::eFetch, SQL_HANDLE_STMT, m_hstmtFrame2, "DoCustomerPositionFrame2");

	//copy output parameters back
	*pFrame2Output = Frame2Output;	//invoke default assignment operator

	// Need to find out here if there are more rows than maximum allowed.
	// Attempt to fetch more. This should normally fail with SQL_NO_DATA.
	//
	rc = SQLFetch(m_hstmtFrame2);
	if (rc != SQL_NO_DATA && rc != SQL_ERROR)
	{	// Set to max allowed value + 1. The harness will return appropriate status code.
		//
		pFrame2Output->hist_len = max_hist_len + 1;
	}

	//if ((rc = SQLCloseCursor(m_hstmtFrame2)) == SQL_ERROR)
	//	ThrowError(CODBCERR::eCloseCursor, SQL_HANDLE_STMT, m_hstmtFrame2, "DoCustomerPositionFrame2");

	// Need two SQLFreeStmt calls for some reason to clean the statement
	// handle properly after the server sent "Transaction count incorrect after batch" message
	// with the result set.
	//
	rc = SQLFreeStmt(m_hstmtFrame2, SQL_CLOSE);
	rc = SQLFreeStmt(m_hstmtFrame2, SQL_CLOSE);
};

void CCustomerPositionDB::DoCustomerPositionFrame3()
{
	CommitTransaction();
};

void CCustomerPositionDB::Cleanup(void* pException)
{
	// For some reason, SQLCloseCursor doesn't work here if
	// deadlock error is reported by SQLMoreResults.
	// Use SQLFreeStmt instead.
	//
	SQLFreeStmt(m_hstmtFrame1, SQL_CLOSE);
	SQLFreeStmt(m_hstmtFrame2, SQL_CLOSE);
};
