This article is just an extension to the CODBCRecordset class article by Stefan Chekanov. It will describe how to add a support for parameterization in that class. For example:
CODBCRecordset tDyn( m_database );
CString cSQL("SELECT * FROM some_table WHERE number_field > ? AND date_field = ?");
tDyn[ "number_field" ] = "12345";
tDyn[ "date_field" ] = "30.03.2001";
tDyn.Open(cSQL, CRecordset::forwardOnly, CRecordset::readOnly);
First problem is that we don�t know what field types are, how many parameters we need (dynamically creating SELECT
statement on user request for specific values in some or all fields�). That will be solved if we say that all parameters will be accepted as LPCTSTR
type, and in the moment of binding parameters (function DoFieldExchange( CFieldExchange* pFX )
) we will know what appropriate types are, and than we can do the right conversion.
We need first to override operator []
in header file
CDBField& operator[]( LPCTSTR szField );
and to add pointer variable that will store the parameter fields
CPtrArray m_ParamFields;
implementation (still in header file)
inline
CDBField& CODBCRecordset::operator[]( LPCTSTR szField )
{
CDBField *fl = new CDBField;
fl->m_cName = szField;
fl->m_pstring = new CString;
fl->m_dwType = DBVT_STRING;
m_ParamFields.Add( fl );
m_nParams++;
return *fl;
}
Very important is that we don't forget to increment CRecordset::m_params
because it will tell the CRecordset
how many parameters to expect.
In .cpp file we need to add the following code to the CODBCRecordset::Clear()
function
if( m_ParamFields.GetSize() > 0 )
{
for( int i = 0; i < m_ParamFields.GetSize(); i++ )
{
CDBField *fl;
fl = (CDBField*) m_ParamFields.GetAt(i);
delete fl;
m_ParamFields.SetAt( i, NULL );
}
m_ParamFields.RemoveAll();
}
and to adopt DoFieldExchange( CFieldExchange* pFX )
function
void CODBCRecordset::DoFieldExchange( CFieldExchange* pFX )
{
CString cFieldName;
if( pFX->m_nOperation != CFieldExchange::BindParam )
{
pFX->SetFieldType( CFieldExchange::outputColumn );
for( UINT i = 0; i < m_nFields; i++ )
{
cFieldName.Format( "[%s]", GetFieldName(i) );
switch( m_fields[i].m_dwType )
{
case DBVT_NULL:
break;
case DBVT_BOOL:
RFX_Bool( pFX, cFieldName, m_fields[i].m_boolVal );
break;
case DBVT_UCHAR:
RFX_Byte( pFX, cFieldName, m_fields[i].m_chVal );
break;
case DBVT_SHORT:
m_fields[i].m_dwType = DBVT_LONG;
RFX_Long( pFX, cFieldName, m_fields[i].m_lVal );
break;
case DBVT_LONG:
RFX_Long( pFX, cFieldName, m_fields[i].m_lVal );
break;
case DBVT_SINGLE:
RFX_Single( pFX, cFieldName, m_fields[i].m_fltVal );
break;
case DBVT_DOUBLE:
RFX_Double( pFX, cFieldName, m_fields[i].m_dblVal );
break;
case DBVT_DATE:
RFX_Date( pFX, cFieldName, *m_fields[i].m_pdate );
break;
case DBVT_STRING:
{
CODBCFieldInfo fi;
GetODBCFieldInfo( (short)i, fi );
RFX_Text( pFX, cFieldName, *m_fields[i].m_pstring, fi.m_nPrecision );
break;
}
case DBVT_BINARY:
RFX_LongBinary( pFX, cFieldName, *(m_fields[i].m_pbinary) );
break;
default:
ASSERT( FALSE );
}
m_fields[i].SetNull( FALSE != IsFieldStatusNull( i ) );
}
}
else
{
if( m_ParamFields.GetSize() == 0 ) return;
pFX->SetFieldType( CFieldExchange::param );
for( int i = 0; i < m_ParamFields.GetSize(); i++ )
{
CDBField *fl = (CDBField*) m_ParamFields.GetAt(i);
unsigned char cValue = (unsigned char)atoi((const char*)fl->m_pstring);
BOOL bValue = atoi((const char*)fl->m_pstring);
long lValue = atol((const char*)fl->m_pstring);
float fValue = (float)atof((const char*)fl->m_pstring);
double dValue = atof((const char*)fl->m_pstring);
cFieldName.Format( "[%s]", fl->m_cName );
switch( fl->m_dwType )
{
case DBVT_NULL:
break;
case DBVT_BOOL:
RFX_Bool( pFX, cFieldName, bValue );
break;
case DBVT_UCHAR:
RFX_Byte( pFX, cFieldName, cValue );
break;
case DBVT_SHORT:
fl->m_dwType = DBVT_LONG;
RFX_Long( pFX, cFieldName, lValue );
break;
case DBVT_LONG:
RFX_Long( pFX, cFieldName, lValue );
break;
case DBVT_SINGLE:
RFX_Single( pFX, cFieldName, fValue );
break;
case DBVT_DOUBLE:
RFX_Double( pFX, cFieldName, dValue );
break;
case DBVT_DATE:
RFX_Date( pFX, cFieldName, *fl->m_pdate);
break;
case DBVT_STRING:
RFX_Text( pFX, cFieldName, *fl->m_pstring );
break;
case DBVT_BINARY:
RFX_LongBinary( pFX, cFieldName, *(fl->m_pbinary) );
break;
default:
ASSERT( FALSE );
}
}
}
}
The important thing here is that we distinguish when we need to bind parameters and when to bind fields.
This is it (hopefully).
History
5 Jul 2001: The CODBCRecordset class now supports all SQL types, not just strings.