Click here to Skip to main content
15,892,697 members
Articles / Desktop Programming / MFC

The Ultimate Toolbox - Updates and User Contributions

Rate me:
Please Sign up or sign in to vote.
4.79/5 (26 votes)
12 Feb 2013CPOL8 min read 255.8K   23.7K   170  
Updates and User Contributions for the Ultimate Toolbox Libraries
// ugrecset.cpp : implementation file
//
//// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement").  Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office.  For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.

#include "stdafx.h"

#include "ugrecset.h"
#include "ugstrop.h"

#include "sqlucode.h"

#include <atlbase.h>
#include <atlconv.h>

//#ifdef _DEBUG
//#define new DEBUG_NEW
//#undef THIS_FILE
//static char THIS_FILE[] = __FILE__;
//#endif


/***************************************************
****************************************************/
CUGRecordset::CUGRecordset(CDatabase* data) : CRecordset(data)
{
	m_pSQLColumnInfoList = NULL;
	m_defaultConnect = "";
}
/***************************************************
****************************************************/
CUGRecordset::~CUGRecordset(){
	CleanUp();

}
/***************************************************
****************************************************/
CString CUGRecordset::GetDefaultSQL(){
	ASSERT_VALID((CObject*)this);

	// Override and add table name or entire SQL SELECT statement
	return _T("");
}
/***************************************************
****************************************************/
void CUGRecordset::CleanUp()
{
	// need to delete all the var objects allocated
	// during BindFields...
	int sz;
	SQLColumnInfo * cfi;

	if(m_pSQLColumnInfoList != NULL) 
	{
		sz = m_pSQLColumnInfoList->GetSize();
		for (int i = 0; i < sz; i++)
		{
			cfi = (SQLColumnInfo *) m_pSQLColumnInfoList->GetAt(i);
			if(NULL == cfi) 
				break;
			if (cfi->pValue != NULL)
			{
				// ok - figure out what it was we allocated...
				switch (cfi->nSQLType)
				{
				case SQL_CHAR:
				case SQL_VARCHAR:
				case SQL_LONGVARCHAR:
				case SQL_WCHAR:
				case SQL_WVARCHAR:
				case SQL_WLONGVARCHAR:
					delete (CString*) cfi->pValue;
					break;
				// boolean
				case SQL_BIT:
					delete (BOOL*) cfi->pValue;
					break;
				// integer
				case SQL_TINYINT:
					delete (unsigned char*) cfi->pValue;
					break;
				case SQL_SMALLINT:
					delete (int *) cfi->pValue;
					break;
				case SQL_INTEGER:
					delete (long *) cfi->pValue;
					break;
				case SQL_BIGINT:
					delete (double *) cfi->pValue;
					break;
				// floating point 
				case SQL_NUMERIC:
				case SQL_DECIMAL:
				case SQL_REAL:
				case SQL_FLOAT:
					delete (float *) cfi->pValue;
					break;
				case SQL_DOUBLE:
					delete (double *) cfi->pValue;
					break;
				// binary 
				case SQL_BINARY:
				case SQL_VARBINARY:
				case SQL_LONGVARBINARY:		
					delete (CByteArray *) cfi->pValue;
					break;
				// date and time
				case SQL_DATE:
				case SQL_TIME:
				case SQL_TIMESTAMP:
					delete (TIMESTAMP_STRUCT *) cfi->pValue;
					break;
				}

				delete cfi;
			}
		}

		delete m_pSQLColumnInfoList;
		m_pSQLColumnInfoList = NULL;
	}
}
/***************************************************
****************************************************/
void CUGRecordset::DoFieldExchange(CFieldExchange* pFX)
{
	//{{AFX_FIELD_MAP(CUGRecordset)
	//}}AFX_FIELD_MAP 
	pFX->SetFieldType( CFieldExchange::outputColumn );
	
	for(UINT i=0;i < m_nFields; i++)
	{
		SQLColumnInfo*	cfi;
		cfi = (SQLColumnInfo *) m_pSQLColumnInfoList->GetAt(i);

		switch(cfi->nSQLType)
		{
		case SQL_NUMERIC:
		case SQL_DECIMAL:
		case SQL_REAL:
		case SQL_FLOAT:
			RFX_Single(pFX,  cfi->strName,
							*(float *) cfi->pValue); 
			break;
		case SQL_DOUBLE:
			RFX_Double(pFX,  cfi->strName,
							*(double *) cfi->pValue); 
			break;
		// strings...
		case SQL_CHAR:
		case SQL_VARCHAR:
		case SQL_WCHAR:
		case SQL_WVARCHAR:
			RFX_Text(pFX,  cfi->strName,
							*(CString *) cfi->pValue);
			break;
		case SQL_LONGVARCHAR:
		case SQL_WLONGVARCHAR:
			RFX_Text(pFX,  cfi->strName,
							*(CString *) cfi->pValue, UG_ODBC_MAXTEXT-1, cfi->nSQLType);// cfi->nScale);
			break;
		// integers
		case SQL_BIT:
			RFX_Bool(pFX,  cfi->strName,
							*(BOOL *) cfi->pValue); 
			break;					
		case SQL_TINYINT:
			RFX_Byte(pFX,  cfi->strName,
							*(unsigned char *) cfi->pValue); 
			break;					
		case SQL_SMALLINT:
//		case SQL_IS_SMALLINT:
			RFX_Int(pFX,  cfi->strName,
							*(int *) cfi->pValue); 
			break;					
		case SQL_INTEGER:
			RFX_Long(pFX,  cfi->strName,
							*(long *) cfi->pValue); 
			break;					
		case SQL_BIGINT:
			RFX_Double(pFX,  cfi->strName,
							*(double *) cfi->pValue); 
			break;					
		// binary 
		case SQL_BINARY:
		case SQL_VARBINARY:
		case SQL_LONGVARBINARY:
			RFX_Binary(pFX,  cfi->strName,
							*(CByteArray *) cfi->pValue,UG_ODBC_MAXBINARY ); 
			break;					
		// date time
		case SQL_DATE:
		case SQL_TIME:
		case SQL_TIMESTAMP:
			RFX_Date(pFX,  cfi->strName,
							*(TIMESTAMP_STRUCT *) cfi->pValue); 
			break;					
		// uh oh...
		default:
			ASSERT(0);
			break;
		}
	}
}
/***************************************************
****************************************************/
BOOL CUGRecordset::Open(UINT nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
{
	USES_CONVERSION ;

	Close();
	TCHAR    func[80];
	RETCODE retcode = 0;

	TRACE(_T("%s\n"), lpszSQL);

	CleanUp();	// deallocate bindings

	// first we execute in order to get info to bind the fields.
	// then, we call open in CRecordset, with our binding in place
	try {
		if(!AllocHstmt())
			ASSERT(0);
		retcode = SQLExecDirect(m_hstmt,(SQLTCHAR*)(lpszSQL),_tcslen(lpszSQL));
	}
	catch(CException* e) {
		e->ReportError();
		e->Delete();
		return FALSE;
	}

	if (retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO){
	UGStr::tcscpy (func,80, _T("SQLExecDirect"));
		ReportSQLError(retcode, func, m_hstmt);
	
		SQLFreeStmt(m_hstmt, SQL_CLOSE);

		return FALSE;
	}


	if(!BindFields(m_hstmt))		// allocates storage etc
		return FALSE;

	SQLFreeStmt(m_hstmt, SQL_CLOSE);
	Close();

	//	m_Database->SetSynchronousMode(TRUE);	//obsolete in MFC 4.2


	try {
		CRecordset::Open(nOpenType,lpszSQL,dwOptions);
	}
	catch(CMemoryException* e){
		#ifdef UG_ODBC_REPORTERROR
			e->ReportError();
		#endif
		e->Delete();
		AfxAbort();
	}
	catch(CDBException* e){
		int er = e->m_nRetCode;
 		TRACE(_T("CDBException %d, caught at line %d in %s:\n'%s'\n"), 
				e->m_nRetCode,__LINE__,_T("ugrecset.cpp"), e->m_strError);
		#ifdef UG_ODBC_REPORTERROR
			TCHAR buf[20];
			UGStr::stprintf(buf,20,_T("Error %d"),er);
			AfxMessageBox(buf);
			e->ReportError();
		#endif
		e->Delete();
		return FALSE;
	}
	catch(CFileException* e) {
		int er = e->m_cause;
		#ifdef UG_ODBC_REPORTERROR
			TCHAR buf[20];
			UGStr::stprintf(buf,20,_T("Error %d"),er);
			AfxMessageBox(buf);
			e->ReportError();
		#endif
		e->Delete();
		return FALSE;
	}
	
	return TRUE;
}
/***************************************************
****************************************************/
CString CUGRecordset::GetDefaultConnect()
{
	if(m_pDatabase == NULL)
		return _T("ODBC:");

	if (!m_defaultConnect.IsEmpty()) {
		return m_defaultConnect;
	} 
	else {
	
		CString dataName = m_pDatabase->GetDatabaseName();
		int backslash = dataName.ReverseFind('\\');
		m_dataFileName ="";
	
		for(int x=backslash+1;x<dataName.GetLength();x++)
			m_dataFileName += dataName[x];

		TCHAR buf[255];
		UGStr::stprintf(buf,255, _T("ODBC;DSN=%s"),m_dataFileName);
		return buf;
	}
}
/***************************************************
****************************************************/
/////////////////////////////////////////////////////////////////////////////
// CUGRecordset diagnostics
#ifdef _DEBUG
void CUGRecordset::AssertValid() const
{
	CRecordset::AssertValid();
}
/***************************************************
****************************************************/
void CUGRecordset::Dump(CDumpContext& dc) const
{
	CRecordset::Dump(dc);
}
#endif //_DEBUG


/***************************************************
****************************************************/
BOOL CUGRecordset::BindFields(HSTMT hstmt) {
	// given a valid statement handle, gather information
	// and allocate space for the RFX function

	SQLTCHAR 	szColName[100];		// returned from SQLDescribeCol...
	SWORD 	cbColName;
	SWORD 	swSqlType;
	SWORD	swScale;
// v7.2 - update 02 - 64-bit
#if _MSC_VER < 1400
	UDWORD	cbColDef;
	SWORD swNullable;
#else
	SQLULEN	cbColDef;
	SQLSMALLINT swNullable;
#endif

	SWORD	swFieldCount;		// ...from SQLNumResultCols
		
	RETCODE retcode;
	
	ASSERT(hstmt);

	// get number of fields
	retcode = ::SQLNumResultCols(hstmt, &swFieldCount);

	if (retcode != SQL_SUCCESS) {
		ReportSQLError(retcode,_T("BindFields"));
		return FALSE;
	}

	// allocate enough room for field info structures
	// in list of pointers to field info structures...

	m_pSQLColumnInfoList = new CPtrArray();
	m_pSQLColumnInfoList->SetSize(swFieldCount);
	
	m_nFields = swFieldCount;

	// determine if the SQLColAttributes call is supported before 
	// entering loop - sqlcaSupported will be TRUE or FALSE.
	UWORD sqlcaSupported;
	::SQLGetFunctions(m_pDatabase->m_hdbc, SQL_API_SQLCOLATTRIBUTES, &sqlcaSupported);

	for(int i = 1; i <= swFieldCount; i++) {

		// get the ODBC 1.0 stuff...
		retcode = ::SQLDescribeCol(hstmt, i, szColName, 100, &cbColName, &swSqlType,
									&cbColDef, &swScale, &swNullable);

		if(retcode != SQL_SUCCESS) {
			ReportSQLError(retcode, _T("BindFields"));
			return FALSE;
		}

		// create a field info struct to hold info
		SQLColumnInfo*	cfi = new SQLColumnInfo;
		

		cfi->strName = szColName;
		cfi->nSQLType = swSqlType;
		cfi->nPrecision = cbColDef;
		cfi->nScale = swScale;
		cfi->nNullable = swNullable;

		// ok - this moves into ODBC 2.0 and 3.0 territory
		// extra info if available!
		if(sqlcaSupported) {
			// some support vars
			SQLCHAR		szCharAttr[256];
			SQLSMALLINT nStringLength;
			// v7.2 - update 02 - 64-bit
#if _MSC_VER < 1400
			SQLINTEGER	nNumericAttr;
#else
			SQLLEN		nNumericAttr;
#endif
			SQLRETURN	retCode;

			// is there a label beyond column name?
			// We already have col name or alias, if
			// returned by SQLDescribeCol...
			retCode = ::SQLColAttribute(m_hstmt, i, 
						SQL_DESC_LABEL, 
						szCharAttr,			// label returned here
						255,				// length of buffer
						&nStringLength,		// length of result
						NULL				// (nNumericAttr)
						);					
			
			if(nStringLength)
				cfi->strLabel = szCharAttr;

			// next lets find out if the field is auto increment
			retcode = ::SQLColAttribute(m_hstmt, i,
						SQL_DESC_AUTO_UNIQUE_VALUE,
						NULL,				// sz buf not needed
						0,
						0,
						&nNumericAttr		// return val here
						);

			if(SQL_SUCCESS == retCode) {
				cfi->nAutoValue = nNumericAttr;
			}
			else {
				cfi->nAutoValue = FALSE;	// don't know...
				TRACE(_T("Warning: AutoIncrement status unknown for field %d\n"), i);
			}

			// lastly, for now, is the field updatable?
			retcode = ::SQLColAttribute(m_hstmt, i,
						SQL_DESC_UPDATABLE,
						NULL,				// sz buf not needed
						0,
						0,
						&nNumericAttr		// return val here
						);

			if(SQL_SUCCESS == retCode) {
				cfi->nUpdatable = nNumericAttr;
			}
			else {
				cfi->nUpdatable = TRUE;	// don't know...
				TRACE(_T("Warning: Updatable status unknown for field %d\n"), i);
			}

		}		


		////////////////////////////////////////////////////////
		// initialize storage for fields
		switch(swSqlType){
		
		// character types
		case SQL_CHAR:
		case SQL_VARCHAR:
		case SQL_LONGVARCHAR:
		case SQL_WCHAR:
		case SQL_WVARCHAR:
		case SQL_WLONGVARCHAR:
			cfi->pValue = (void *) new CString;
			*((CString *) cfi->pValue)->GetBuffer(UG_ODBC_MAXTEXT);
			*((CString *) cfi->pValue) = "";
		break;

		// boolean
		case SQL_BIT:
			cfi->pValue = (void *) new BOOL;
			*((BOOL *) cfi->pValue) = FALSE;
		break;

		// integer
		case SQL_TINYINT:
			cfi->pValue = (void *) new unsigned char;
			*((unsigned char *) cfi->pValue) = '\0';
		break;
		case SQL_SMALLINT:
			cfi->pValue = (void *) new int;
			*((int *) cfi->pValue) = 0;
		break;
		case SQL_INTEGER:
			cfi->pValue = (void *) new long;
			*((long *) cfi->pValue) = 0;
		break;
		case SQL_BIGINT:
			cfi->pValue = (void *) new double;
			*((double *) cfi->pValue) = 0;
		break;

		// floating point 
		case SQL_NUMERIC:
		case SQL_DECIMAL:
		case SQL_REAL:
		case SQL_FLOAT:
			cfi->pValue = (void *) new float;
			*((float *) cfi->pValue) = (float) 0;
		break;
		case SQL_DOUBLE:
			cfi->pValue = (void *) new double;
			*((double *) cfi->pValue) = 0.0;
		break;
		
		// binary 
		case SQL_BINARY:
		case SQL_VARBINARY:
		case SQL_LONGVARBINARY:
			cfi->pValue = (void *) new CByteArray;
			((CByteArray *) cfi->pValue)->SetSize(UG_ODBC_MAXBINARY);
		break;
		// date and time.  CString limited, as is OLEDate variant.
		// Though there are DATE_ and TIME_STRUCTs, RFX_DATE uses
		// TIMESTAMP_STRUCT so all these mapped to same...
		case SQL_DATE: 
		case SQL_TIME:
		case SQL_TIMESTAMP:
			cfi->pValue = (void *) new TIMESTAMP_STRUCT;
			memset(cfi->pValue, 0, sizeof(TIMESTAMP_STRUCT));
			break;
		break;

		// oops...
		default:
			//ASSERT(0);
			return FALSE;
		break;
		}
		// v7.2 - update 02 - 64-bit - change TD
		// if(cfi == NULL) _asm int 3;
		ASSERT(cfi);
		m_pSQLColumnInfoList->SetAt(i-1,cfi);
	}

	return TRUE;
}

/***************************************************
****************************************************/
void CUGRecordset::ReportSQLError(RETCODE retcode, LPCTSTR func, HSTMT hstmt) {

	TCHAR  buf[512];

	SQLTCHAR  sqlerror[255];
	SQLTCHAR  sqlstate[255];
	SDWORD nerr;
	SWORD num;
	RETCODE rc;

	switch (retcode) {
	case SQL_SUCCESS: {

		//AfxMessageBox("SQLExecDirect returned success ");
		//AfxMessageBox((LPCSTR)buf);
		break;
					  }
	case SQL_SUCCESS_WITH_INFO:{
		//AfxMessageBox("SQLExecDirect returned success with info");
		// this indicates that there is a driver specific warning
		// to be investigated...
		rc = SQLError(SQL_NULL_HENV,m_pDatabase->m_hdbc,SQL_NULL_HSTMT,sqlstate,
							&nerr,sqlerror,254,&num);

		if (rc == SQL_NO_DATA_FOUND || rc == SQL_ERROR)
			UGStr::stprintf(buf,512,_T("An unknown error occurred in %s"), func);
		else 
			UGStr::stprintf(buf,512,_T("SQL_ERROR retcode in function:%s  SqlState: %s\nError: %s"), func, sqlstate, sqlerror);
		

		AfxMessageBox(buf);
		break;
							   }
	case SQL_NEED_DATA: {
		UGStr::stprintf(buf,512,_T("%s returned SQL_NEED_DATA"), func);
		AfxMessageBox(buf);
		break;
						}
	case SQL_STILL_EXECUTING: {
		UGStr::stprintf(buf,512,_T("%s  returned SQL_STILL_EXECUTING"), func);
		AfxMessageBox(buf);
		break;
							  }
	case SQL_ERROR:{
		rc = SQLError(SQL_NULL_HENV,m_pDatabase->m_hdbc,hstmt,sqlstate,
							&nerr,sqlerror,254,&num);
		if (rc == SQL_NO_DATA_FOUND || rc == SQL_ERROR)
			UGStr::stprintf(buf,512,_T("An unknown error occurred in %s"), func);
		else 
			UGStr::stprintf(buf,512,_T("SQL_ERROR retcode in function:%s  SqlState: %s\nError: %s"), func, sqlstate, sqlerror);
		
		AfxMessageBox(buf);

		break;
				   }
	case SQL_INVALID_HANDLE:{
		UGStr::stprintf(buf,512,_T("%s returned SQL_INVALID_HANDLE"), func);
		AfxMessageBox(buf);
		break;
							}
	default:
		break;
	}

}


SQLColumnInfo* CUGRecordset::GetFieldInfo(int col){
	// return a pointer to the field info structure for this column
	if (col <= (int)m_nFields)
		return (SQLColumnInfo *) m_pSQLColumnInfoList->GetAt(col);
	else
		return NULL;
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
In January 2005, David Cunningham and Chris Maunder created TheUltimateToolbox.com, a new group dedicated to the continued development, support and growth of Dundas Software’s award winning line of MFC, C++ and ActiveX control products.

Ultimate Grid for MFC, Ultimate Toolbox for MFC, and Ultimate TCP/IP have been stalwarts of C++/MFC development for a decade. Thousands of developers have used these products to speed their time to market, improve the quality of their finished products, and enhance the reliability and flexibility of their software.
This is a Organisation

476 members

Comments and Discussions