#include "stdafx.h"
#include "ODBCWrappers.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


//The ODBC_CHECK_RETURN2 macro checks and sets the return value from ODBC calls
#define ODBC_CHECK_RETURN2(handle) \
  handle.ValidateReturnValue(m_nRet); \
  if ((m_nRet != SQL_SUCCESS) && (m_nRet != SQL_SUCCESS_WITH_INFO)) \
  { \
    m_RETURNVALUE = UnknownODBCError; \
    handle.GetDiagRecords(m_sDiagRecords); \
    return m_RETURNVALUE; \
  }

//Another flavour of an ODBC_CHECK_RETURN macro
#define ODBC_CHECK_RETURN3(nRet, handle) \
  handle.ValidateReturnValue(nRet); \
  if ((nRet != SQL_SUCCESS) && (nRet != SQL_SUCCESS_WITH_INFO)) \
  { \
    return nRet; \
  }


//test ODBC accessor class used for demonstrating calling a stored procedure
class CdboPJTestAccessor
{
public:
//Enums
  enum SPError
  {
    UnknownODBCError = -1000,
    NoRowsetReturned = -1002,
    NotRun = -1004,
  };

//Parameter values  
	LONG m_RETURNVALUE;
	LONG m_nValue;
	bool m_bValue;
	TCHAR m_nChar;
	BYTE m_nX;
	SQLLEN m_nIndicatorX;
	float m_fReal;
	double m_fFloat;
	BYTE m_byTinyInt;
	short m_nSmallInt;
	__int64 m_nBigInt;
	BYTE m_byBinary[1024];
	SQLINTEGER m_nBinaryLength;
	SQLLEN m_nBinaryStatus;
	SQL_NUMERIC_STRUCT m_Decimal;
	SQLLEN m_DecimalStatus;
	TCHAR m_sDecimal[64];
	BYTE m_byBinaryOut[1024];
	SQLINTEGER m_nBinaryOutLength;
	SQLLEN m_nBinaryOutStatus;
	SQL_NUMERIC_STRUCT m_DecimalOut;
	TCHAR m_sDecimalOut[64];
	SQLLEN m_nDecimalOutStatus;
	double m_fDecimalOut;
	
//Column values
	LONG  m_colfldAddressID;
	TCHAR m_colfldAddressLine1[61];
	TCHAR m_colfldAddressLine2[61];
	TCHAR m_colfldCity[31];
	LONG  m_colfldStateProvinceID;
	TCHAR m_colfldPostalCode[16];
	GUID  m_colfldrowguid;
	TCHAR  m_colfldChar;
	TIMESTAMP_STRUCT m_colfldModifiedDate;
	
BEGIN_ODBC_PARAM_MAP(CdboPJTestAccessor)
	SET_ODBC_PARAM_TYPE(SQL_PARAM_OUTPUT)
	ODBC_PARAM_ENTRY(1, m_RETURNVALUE)
	SET_ODBC_PARAM_TYPE(SQL_PARAM_INPUT)
	ODBC_PARAM_ENTRY(2, m_nValue)
	ODBC_PARAM_ENTRY(3, m_bValue)
	ODBC_PARAM_ENTRY(4, m_nChar)
	ODBC_PARAM_ENTRY(5, m_fReal)
	ODBC_PARAM_ENTRY(6, m_fFloat)
	ODBC_PARAM_ENTRY(7, m_byTinyInt)
	ODBC_PARAM_ENTRY(8, m_nSmallInt)
	ODBC_PARAM_ENTRY(9, m_nBigInt)
	ODBC_PARAM_LENGTH_STATUS(10, m_byBinary, m_nBinaryLength, m_nBinaryStatus)
	ODBC_PARAM_PS_LENGTH_STATUS(11, m_sDecimal, 2, 18, m_DecimalStatus)
	SET_ODBC_PARAM_TYPE(SQL_PARAM_INPUT_OUTPUT)
	ODBC_PARAM_ENTRY_STATUS(12, m_nX, m_nIndicatorX)
	ODBC_PARAM_LENGTH_STATUS(13, m_byBinaryOut, m_nBinaryOutLength, m_nBinaryOutStatus)
	ODBC_PARAM_ENTRY_STATUS(14, m_DecimalOut, m_nDecimalOutStatus)
END_ODBC_PARAM_MAP()

BEGIN_ODBC_COLUMN_MAP(CdboPJTestAccessor)
	ODBC_COLUMN_ENTRY(1, m_colfldAddressID)
	ODBC_COLUMN_ENTRY(2, m_colfldAddressLine1)
	ODBC_COLUMN_ENTRY(3, m_colfldAddressLine2)
	ODBC_COLUMN_ENTRY(4, m_colfldCity)
	ODBC_COLUMN_ENTRY(5, m_colfldStateProvinceID)
	ODBC_COLUMN_ENTRY(6, m_colfldPostalCode)
  ODBC_COLUMN_ENTRY(7, m_colfldrowguid)
  ODBC_COLUMN_ENTRY(8, m_colfldModifiedDate)
  ODBC_COLUMN_ENTRY(9, m_colfldChar)
END_ODBC_COLUMN_MAP()	
	
DEFINE_ODBC_COMMAND(CdboPJTestAccessor, _T("{ ? = CALL dbo.PJ_Test(?,?,?,?,?,?,?,?,?,?,?,?,?) }"))
		
	//You may wish to call this function if you are inserting a record and wish to
	//initialize all the fields, if you are not going to explicitly set all of them.
	void ClearRecord()
	{
		memset(this, 0, sizeof(*this));
	}	
};


//Another test accessor class
class CdboPJTest2Accessor
{
public:
  TCHAR m_szTableName[128];
};


//test class used for demonstrating calling a stored procedure
class CdboPJTest : public CODBC::CAccessor<CdboPJTestAccessor>
{
public:
//Constructors / Destructors
  explicit CdboPJTest(CODBC::CConnection* pDbConnect) : m_nRet(SQL_SUCCESS), 
                                                        m_pDbConnect(pDbConnect)
  {
    ATLASSUME(m_pDbConnect != NULL);

		ClearRecord();
    m_RETURNVALUE = NotRun;
  }

//Methods
	int RunStoredProcedure()
  {
    //Validate our parameters
    ATLASSUME(m_pDbConnect);

    //Create the statement object
    CODBC::CStatement statement;
    m_nRet = statement.Create(*m_pDbConnect);
    ODBC_CHECK_RETURN2(statement);

    m_nRet = statement.ColumnPrivileges(NULL, NULL, _T("Person.Address"), NULL);
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.Columns(NULL, _T("Person"), _T("Address"), _T("AddressLine1"));
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.Tables(NULL, _T("Person"), _T("Address"), NULL);
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.Procedures(NULL, _T("dbo"), _T("PJ_Test"));
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.ProcedureColumns(NULL, _T("dbo"), _T("PJ_Test"), NULL);
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.PrimaryKeys(NULL, _T("Person"), _T("Address"));
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.ForeignKeys(NULL, _T("State"), _T("Province"), NULL, _T("Person"), _T("Address"));
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.TablePrivileges(NULL, _T("Person"), NULL);
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.Statistics(NULL, _T("Person"), _T("Address"), SQL_INDEX_ALL, SQL_ENSURE);
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.SpecialColumns(NULL, _T("Person"), _T("Address"), SQL_SCOPE_CURROW, SQL_NULLABLE);
    ODBC_CHECK_RETURN2(statement);
    statement.Cancel();
    m_nRet = statement.GetTypeInfo(SQL_DECIMAL);
    ODBC_CHECK_RETURN2(statement);

    CODBC::CDynamicColumnAccessor<CdboPJTest2Accessor> accessor;
    accessor.BindColumns(statement);

    //Iterate through the returned recordset
    BOOL bContinue = TRUE;
    for (int i=0; bContinue; i++) 
    {
      m_nRet = statement.FetchNext();
      if (m_nRet != SQL_SUCCESS && m_nRet != SQL_SUCCESS_WITH_INFO)
        bContinue = FALSE;
      else
      {
        SQLSMALLINT nColumn1 = accessor.GetColumnNo(_T("TABLE_NAME"));
        nColumn1;
        SQLSMALLINT nColumn2 = accessor.GetColumnNo(_T("COLUMN_NAME"));
        nColumn2;
        SQLRETURN nRet = accessor.GetValue(statement, 1, accessor.m_szTableName);
        ODBC_CHECK_RETURN(nRet, statement);
      }
    }   
    statement.Free(SQL_CLOSE);

    SQLUINTEGER nAttributes = 0;
    m_nRet = m_pDbConnect->GetInfo(SQL_DYNAMIC_CURSOR_ATTRIBUTES1, &nAttributes, sizeof(nAttributes), NULL);
    ODBC_CHECK_RETURN2(statement);
    SQL_CA1_NEXT;
    m_nRet = statement.SetAttr(SQL_ATTR_CURSOR_TYPE, SQL_CURSOR_DYNAMIC);
    ODBC_CHECK_RETURN2(statement);
    m_nRet = statement.SetAttr(SQL_ATTR_CURSOR_SCROLLABLE, SQL_SCROLLABLE);
    ODBC_CHECK_RETURN2(statement);
    m_nRet = statement.SetAttr(SQL_ATTR_CONCURRENCY, SQL_CONCUR_ROWVER);
    ODBC_CHECK_RETURN2(statement);
    m_nRet = statement.SetAttr(SQL_ATTR_CURSOR_SENSITIVITY, SQL_INSENSITIVE);
    ODBC_CHECK_RETURN2(statement);

    //Fill in the member variables which we will be binding to
    m_RETURNVALUE = 0;
    m_nValue = 33;
    m_bValue = true;
    m_nChar = 12;
    m_nX = 55;
    m_nIndicatorX = 0;
    m_fReal = static_cast<float>(12.3);
    m_fFloat = 15.7;
    m_byTinyInt = 33;
    m_nSmallInt = 44;
    m_nBigInt = 77;
    memset(m_byBinary, 0x31, sizeof(m_byBinary));
    m_nBinaryLength = 4;
    m_nBinaryStatus = 4;
    m_nBinaryOutLength = sizeof(m_byBinaryOut);
    
    m_Decimal.precision = 18;
    m_Decimal.scale = 2;
    m_Decimal.sign = 1;
    memset(m_Decimal.val, 0, sizeof(m_Decimal.val));
    m_DecimalStatus = SQL_NTS;
  #ifdef _tcscpy_s
    _tcscpy_s(m_sDecimal, sizeof(m_sDecimal)/sizeof(TCHAR), _T("123.45"));
  #else
    _tcscpy(m_sDecimal, _T("123.45"));
  #endif
    m_nDecimalOutStatus = SQL_NULL_DATA;

		//Prepare the statement
		m_nRet = statement.Prepare(GetDefaultCommand());
    ODBC_CHECK_RETURN2(statement);

    //Bind the parameters
    m_nRet = BindParameters(statement);
    ODBC_CHECK_RETURN2(statement);

    //Execute the statement
    m_nRet = statement.Execute();
    ODBC_CHECK_RETURN2(statement);
    
    //Check the return value
    if (m_RETURNVALUE != 0)
      return m_RETURNVALUE;

    //Bind the columns
    m_nRet = BindColumns(statement);
    ODBC_CHECK_RETURN2(statement);

    SQLHDESC hArd0 = 0, hIrd0 = 0;
    m_nRet = statement.GetAttr(SQL_ATTR_APP_ROW_DESC, &hArd0, 0, NULL);
    ODBC_CHECK_RETURN2(statement);
    m_nRet = statement.GetAttr(SQL_ATTR_IMP_ROW_DESC, &hIrd0, 0, NULL);
    ODBC_CHECK_RETURN2(statement);
    CODBC::CDescriptor descriptor1;
    descriptor1.Attach(hArd0);
    CODBC::CDescriptor descriptor2;
    descriptor2.Attach(hIrd0);

    CODBC::CDescriptor descriptor3;
    m_nRet = descriptor3.Create(*m_pDbConnect);
    descriptor3.ValidateReturnValue(m_nRet);
    m_nRet = descriptor1.Copy(descriptor3);
    descriptor1.ValidateReturnValue(m_nRet);
    SQLSMALLINT nField = 0;
    SQLINTEGER nSize = 0;
    m_nRet = descriptor3.GetField(0, SQL_DESC_ALLOC_TYPE, &nField, sizeof(nField), &nSize);  
    descriptor3.ValidateReturnValue(m_nRet);
    SQLTCHAR szName[128];
    szName[0] = _T('\0');
    SQLSMALLINT nStringLength = 0;
    SQLSMALLINT nType = 0;
    SQLSMALLINT nSubType = 0;
    SQLLEN      nLength = 0;
    SQLSMALLINT nPrecision = 0;
    SQLSMALLINT nScale = 0;
    SQLSMALLINT nNullable = 0;
    m_nRet = descriptor3.GetRec(1, szName, sizeof(szName)/sizeof(SQLTCHAR), &nStringLength, &nType, &nSubType, &nLength, &nPrecision, &nScale, &nNullable);
    descriptor3.ValidateReturnValue(m_nRet);
    m_nRet = descriptor3.SetRec(1, nType, nSubType, nLength, nPrecision, nScale, NULL, NULL, NULL);
    descriptor3.ValidateReturnValue(m_nRet);
    descriptor3.SetField(0, SQL_DESC_ALLOC_TYPE, &nField, sizeof(nField));
    descriptor3.ValidateReturnValue(m_nRet);

    //Check we have got back a results set
    SQLSMALLINT nColumns = 0;
    m_nRet = statement.NumResultCols(&nColumns);
    ODBC_CHECK_RETURN2(statement);
    if (nColumns == 0)
    {
      m_RETURNVALUE = NoRowsetReturned;
      return m_RETURNVALUE;
    }

  #ifdef _DEBUG
    SQLSMALLINT nNumParams = 0;
    SQLRETURN nRet = statement.NumParams(&nNumParams);
    nRet;
    SQLLEN nTemp = 0;
    m_nRet = statement.ColAttribute(3, SQL_DESC_UNNAMED, NULL, SQL_IS_POINTER, NULL, &nTemp);
    ODBC_CHECK_RETURN2(statement);
  #endif  

    //Iterate through the returned recordset
    bContinue = TRUE;
    for (int j=0; bContinue; j++) 
    {
      ClearRecord();
      m_nRet = statement.FetchNext();
      if (m_nRet != SQL_SUCCESS && m_nRet != SQL_SUCCESS_WITH_INFO)
        bContinue = FALSE;
      else
      {
	      //m_nRet = GetData(statement); //Used if using a dynamic accessor
        ODBC_CHECK_RETURN2(statement);
      }
    }  
        
    return m_RETURNVALUE;
  }

  /* //Used if using a dynamic accessor
  SQLRETURN GetData(CODBC::CStatement& statement)
  {
    SQLINTEGER nColumn = GetColumnNo(_T("AddressID"));
    nColumn = GetColumnNo(_T("AddressLine1"));
    nColumn = GetColumnNo(_T("AddressLine2"));
    nColumn = GetColumnNo(_T("City"));
    nColumn = GetColumnNo(_T("StateProvinceID"));
    nColumn = GetColumnNo(_T("PostalCode"));
    nColumn = GetColumnNo(_T("rowguid"));
    nColumn = GetColumnNo(_T("ModifiedDate"));
  
    SQLRETURN nRet = GetValue(statement, 1, m_colfldAddressID);
    ODBC_CHECK_RETURN(nRet, statement);
    nRet = GetValue(statement, 2, m_colfldAddressLine1);
    ODBC_CHECK_RETURN(nRet, statement);
    nRet = GetValue(statement, 3, m_colfldAddressLine2);
    ODBC_CHECK_RETURN(nRet, statement);
    nRet = GetValue(statement, 4, m_colfldCity);
    ODBC_CHECK_RETURN(nRet, statement);
    nRet = GetValue(statement, 5, m_colfldStateProvinceID);
    ODBC_CHECK_RETURN(nRet, statement);
    nRet = GetValue(statement, 6, m_colfldPostalCode);
    ODBC_CHECK_RETURN(nRet, statement);
    nRet = GetValue(statement, 7, m_colfldrowguid);
    ODBC_CHECK_RETURN(nRet, statement);
    nRet = GetValue(statement, 8, m_colfldModifiedDate);
    ODBC_CHECK_RETURN(nRet, statement);
    nRet = GetValue(statement, 9, m_colfldChar);
    return nRet;
	}
	*/
		
//Member variables
  SQLRETURN                             m_nRet;
  CODBC::CConnection*                   m_pDbConnect;
#ifdef CODBCWRAPPERS_MFC_EXTENSIONS
  CArray<CODBC::String, CODBC::String&> m_sDiagRecords;
#else
  std::vector<CODBC::String>            m_sDiagRecords;
#endif
};


//test ODBC accessor class used for demonstrating inserting a row into AdventureWorks2008.Production.ProductCategory table
class CdboProductCategoryInsertAccessor
{
public:
//Parameter values  
	TCHAR m_sProductCategory[51];
	
BEGIN_ODBC_PARAM_MAP(CdboProductCategoryInsertAccessor)
	SET_ODBC_PARAM_TYPE(SQL_PARAM_INPUT)
	ODBC_PARAM_ENTRY(1, m_sProductCategory)
END_ODBC_PARAM_MAP()

DEFINE_ODBC_COMMAND(CdboProductCategoryInsertAccessor, _T("INSERT INTO Production.ProductCategory (Name) VALUES (?)"))
		
	//You may wish to call this function if you are inserting a record and wish to
	//initialize all the fields, if you are not going to explicitly set all of them.
	void ClearRecord()
	{
		memset(this, 0, sizeof(*this));
	}	
};


//test class used for demonstrating inserting a row into  AdventureWorks2008.Production.ProductCategory table
class CdboProductCategoryInsert : public CODBC::CAccessor<CdboProductCategoryInsertAccessor>
{
public:
//Methods
	SQLRETURN Insert(CODBC::CConnection* pDbConnect)
  {
    //Validate our parameters
    ATLASSUME(pDbConnect);

    //Create the statement object
    CODBC::CStatement statement;
    SQLRETURN nRet = statement.Create(*pDbConnect);
    ODBC_CHECK_RETURN3(nRet, statement);

		//Prepare the statement
		nRet = statement.Prepare(GetDefaultCommand());
    ODBC_CHECK_RETURN3(nRet, statement);

    //Bind the parameters
  #ifdef _tcscpy_s
    _tcscpy_s(m_sProductCategory, sizeof(m_sProductCategory)/sizeof(TCHAR), _T("Unicycles"));
  #else
    _tcscpy(m_sProductCategory, _T("Unicycles"));
  #endif
    nRet = BindParameters(statement);
    ODBC_CHECK_RETURN3(nRet, statement);

    //Execute the statement
    nRet = statement.Execute();
    ODBC_CHECK_RETURN3(nRet, statement);
    return nRet;
  }
};


//test ODBC accessor class used for demonstrating returning rows from AdventureWorks2008.Production.ProductCategory table
class CdboProductCategoryIterateAccessor
{
public:
//Parameter values  
  LONG m_colfldProductCategoryID;
	TCHAR m_colfldProductCategory[51];
	GUID m_colfldrowguid;
	SQL_TIMESTAMP_STRUCT m_colfldModifiedDate;

BEGIN_ODBC_PARAM_MAP(CdboProductCategoryIterateAccessor)
END_ODBC_PARAM_MAP()  

BEGIN_ODBC_COLUMN_MAP(CdboProductCategoryIterateAccessor)
	ODBC_COLUMN_ENTRY(1, m_colfldProductCategoryID)
	ODBC_COLUMN_ENTRY(2, m_colfldProductCategory)
	ODBC_COLUMN_ENTRY(3, m_colfldrowguid)
	ODBC_COLUMN_ENTRY(4, m_colfldModifiedDate)
END_ODBC_COLUMN_MAP()	

DEFINE_ODBC_COMMAND(CdboProductCategoryIterateAccessor, _T("SELECT ProductCategoryID, Name, rowguid, ModifiedDate FROM Production.ProductCategory"))
		
	//You may wish to call this function if you are inserting a record and wish to
	//initialize all the fields, if you are not going to explicitly set all of them.
	void ClearRecord()
	{
		memset(this, 0, sizeof(*this));
	}	
};


//test class used for demonstrating iterating across the AdventureWorks2008.Production.ProductCategory table
class CdboProductCategoryIterate : public CODBC::CAccessor<CdboProductCategoryIterateAccessor>
{
public:
//Methods
	SQLRETURN Iterate(CODBC::CConnection* pDbConnect)
  {
    //Validate our parameters
    ATLASSUME(pDbConnect);

    //Create the statement object
    CODBC::CStatement statement;
    SQLRETURN nRet = statement.Create(*pDbConnect);
    ODBC_CHECK_RETURN3(nRet, statement);

		//Prepare the statement
		nRet = statement.Prepare(GetDefaultCommand());
    ODBC_CHECK_RETURN3(nRet, statement);

    //Bind the parameters
    nRet = BindParameters(statement);
    ODBC_CHECK_RETURN3(nRet, statement);

    //Execute the statement
    nRet = statement.Execute();
    ODBC_CHECK_RETURN3(nRet, statement);
    
    //Bind the columns
    nRet = BindColumns(statement);
    ODBC_CHECK_RETURN3(nRet, statement);

    //Iterate through the returned recordset
    BOOL bContinue = TRUE;
    for (int i=0; bContinue; i++) 
    {
      ClearRecord();
      nRet = statement.FetchNext();
      if (nRet != SQL_SUCCESS && nRet != SQL_SUCCESS_WITH_INFO)
        bContinue = FALSE;
    }  
    
    return SQL_SUCCESS;
  }
};


//test ODBC accessor class used for demonstrating updating a row in AdventureWorks2008.Production.ProductCategory table
class CdboProductCategoryUpdateAccessor
{
public:
//Parameter values  
	TCHAR m_sProductCategory[51];
	
BEGIN_ODBC_PARAM_MAP(CdboProductCategoryUpdateAccessor)
	SET_ODBC_PARAM_TYPE(SQL_PARAM_INPUT)
	ODBC_PARAM_ENTRY(1, m_sProductCategory)
END_ODBC_PARAM_MAP()

DEFINE_ODBC_COMMAND(CdboProductCategoryInsertAccessor, _T("UPDATE Production.ProductCategory SET Name = ? WHERE Name='Unicycles'"))
		
	//You may wish to call this function if you are inserting a record and wish to
	//initialize all the fields, if you are not going to explicitly set all of them.
	void ClearRecord()
	{
		memset(this, 0, sizeof(*this));
	}	
};


//test class used for demonstrating updating a row in AdventureWorks2008.Production.ProductCategory table
class CdboProductCategoryUpdate : public CODBC::CAccessor<CdboProductCategoryUpdateAccessor>
{
public:
//Methods
	SQLRETURN Update(CODBC::CConnection* pDbConnect)
  {
    //Validate our parameters
    ATLASSUME(pDbConnect);

    //Create the statement object
    CODBC::CStatement statement;
    SQLRETURN nRet = statement.Create(*pDbConnect);
    ODBC_CHECK_RETURN3(nRet, statement);

		//Prepare the statement
		nRet = statement.Prepare(GetDefaultCommand());
    ODBC_CHECK_RETURN3(nRet, statement);

    //Bind the parameters
  #ifdef _tcscpy_s
    _tcscpy_s(m_sProductCategory, sizeof(m_sProductCategory)/sizeof(TCHAR), _T("Tricycles"));
  #else
    _tcscpy(m_sProductCategory, _T("Tricycles"));
  #endif
    nRet = BindParameters(statement);
    ODBC_CHECK_RETURN3(nRet, statement);

    //Execute the statement
    nRet = statement.Execute();
    ODBC_CHECK_RETURN3(nRet, statement);
    return nRet;
  }
};


//test class used for demonstrating deleting a row from the AdventureWorks2008.Production.ProductCategory table
class CdboProductCategoryDelete
{
public:
//Methods
	SQLRETURN Delete(CODBC::CConnection* pDbConnect)
  {
    //Validate our parameters
    ATLASSUME(pDbConnect);

    //Create the statement object
    CODBC::CStatement statement;
    SQLRETURN nRet = statement.Create(*pDbConnect);
    ODBC_CHECK_RETURN3(nRet, statement);

		//Prepare the statement
		nRet = statement.Prepare(reinterpret_cast<SQLTCHAR*>(_T("DELETE FROM Production.ProductCategory WHERE Name = 'Tricycles'")));
    ODBC_CHECK_RETURN3(nRet, statement);

    //Execute the statement
    nRet = statement.Execute();
    ODBC_CHECK_RETURN3(nRet, statement);
    return nRet;
  }
};


void _tmain()
{
#ifdef CODBCWRAPPERS_MFC_EXTENSIONS
  // initialize MFC and print and error on failure
	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
	{
		printf(_T("Fatal Error: MFC initialization failed\n"));
		return;
	}
#endif

  //Setup the environment
  CODBC::CEnvironment env;
  SQLRETURN nRet = env.Create();
  env.ValidateReturnValue(nRet);
  nRet = env.SetAttr(SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3);
  env.ValidateReturnValue(nRet);  
  
  nRet = env.SetAttrU(SQL_ATTR_CONNECTION_POOLING, SQL_CP_OFF);
  env.ValidateReturnValue(nRet);  
  
  SQLINTEGER nVersion;
  nRet = env.GetAttr(SQL_ATTR_ODBC_VERSION, nVersion);
  env.ValidateReturnValue(nRet);

  SQLUINTEGER nPooling;
  nRet = env.GetAttrU(SQL_ATTR_CONNECTION_POOLING, nPooling);
  env.ValidateReturnValue(nRet);

  nRet = env.CommitTran();
  env.ValidateReturnValue(nRet);
  nRet = env.RollbackTran();
  env.ValidateReturnValue(nRet);


  //Setup the connection
  CODBC::CConnection con;
  nRet = con.Create(env);
  con.ValidateReturnValue(nRet);

  CODBC::String sDesc;
#ifdef CODBCWRAPPERS_MFC_EXTENSIONS
  CArray<CODBC::String, CODBC::String&> sAttributes;
#else
  std::vector<CODBC::String> sAttributes;
#endif
  nRet = env.Drivers(SQL_FETCH_FIRST, sDesc, sAttributes);

  CODBC::String sServer;
  nRet = env.DataSources(SQL_FETCH_FIRST, sServer, sDesc);
 
  SQLHANDLE h = con.Detach();
  con.Attach(h);
  CODBC::CConnection h3;
  h3 = con;
  con.Attach(h3.Detach());

  //Set login timeout to 5 seconds
  nRet = con.SetAttrU(SQL_ATTR_LOGIN_TIMEOUT, 5);
  con.ValidateReturnValue(nRet);


  //Connect to the datasource (Assumes AdventureWorks2008 sample database is installed and made available as a DSN called "AdventureWorks")
  CODBC::String sFullInitString;
  nRet = con.DriverConnect(reinterpret_cast<SQLTCHAR*>(_T("DSN=AdventureWorks;")), sFullInitString);
  CODBC::String sTemp;
  //nRet = con.BrowseConnect(_T("DRIVER={SQL Native Client}"), sTemp);
  //nRet = con.Connect(_T("SQLLOCAL"), NULL, NULL);
  con.ValidateReturnValue(nRet);
  
  nRet = con.GetAttr(SQL_ATTR_CURRENT_CATALOG, sTemp);
  con.ValidateReturnValue(nRet);

  SQLUINTEGER nTemp = 0;
  nRet = con.GetAttrU(SQL_ATTR_PACKET_SIZE, nTemp);
  con.ValidateReturnValue(nRet);
  
  SQLINTEGER nTemp2 = 0;
  nRet = con.GetAttr(SQL_ATTR_TRANSLATE_OPTION, nTemp2);
  con.ValidateReturnValue(nRet);

  nRet = con.NativeSql(_T("SELECT { fn CONVERT (empid, SQL_SMALLINT) } FROM employee"), sTemp);
  con.ValidateReturnValue(nRet);
  
  //nRet = con.SetAttr(SQL_ATTR_CURRENT_CATALOG, _T("AdventureWorks"));
  //con.ValidateReturnValue(nRet);

  nRet = con.GetAttr(SQL_ATTR_CURRENT_CATALOG, sTemp);
  con.ValidateReturnValue(nRet);
  
  nRet = con.CommitTran();
  con.ValidateReturnValue(nRet);
  nRet = con.RollbackTran();
  con.ValidateReturnValue(nRet);

  CODBC::CDescriptor descriptor;
  nRet = descriptor.Create(con);
  descriptor.ValidateReturnValue(nRet);

  SQLUSMALLINT nFunctions = 0;
  nRet = con.GetFunctions(SQL_API_SQLTABLES, &nFunctions);
  con.ValidateReturnValue(nRet);
  

  //Run our test stored procedure (Can be created using the "Create PJ_Test SP.sql" script)
  CdboPJTest sp(&con);
  int nReturn = sp.RunStoredProcedure();
  ATLASSERT(nReturn == 0);
  UNREFERENCED_PARAMETER(nReturn);
 
 
  //Test out adding a row to AdventureWorks2008.Production.ProductCategory table 
  CdboProductCategoryInsert table;
  table.Insert(&con);
 
  
  //Test out iterating across all rows in the AdventureWorks2008.Production.ProductCategory table
  CdboProductCategoryIterate table2;
  table2.Iterate(&con);
 
 
  //Test out updating a row in the AdventureWorks2008.Production.ProductCategory table
  CdboProductCategoryUpdate table3;
  table3.Update(&con);
 
 
  //Test deleting a row from the AdventureWorks2008.Production.ProductCategory table
  CdboProductCategoryDelete table4;
  table4.Delete(&con);
 
 
  //Disconnect
  con.Disconnect();
}
