Using Recordsets Without Binding

All three of the examples in this chapter used recordset classes derived from CRecordset. The data members of those classes were bound to database columns at recordset creation time using the ODBC binding mechanism. When the programs called CRecordset::Move, the ODBC driver copied data directly from the data source into the data members.

ODBC has always supported unbound data access through the functions SQLGetData and SQLPutData. Now the CRecordset class supports read-only unbound data access through its GetFieldValue member function. One overloaded version of this function retrieves the value of a field specified by name and then stores it in an object of class CDBVariant. This class is similar to the COleVariant class described in Chapter 25, but it does not use any OLE code and it doesn't have as many overloaded operators and member functions. The COleVariant class has a data member, m_dwType, followed by a union. If the type code is DBVT_LONG, for example, you access an integer in the union member m_lVal.

You can use CRecordset::GetFieldValue for circumstances in which you don't know the database schema at design time. Your "dynamic database" program constructs an object of class CRecordset, and you access the column values with code like this:

void CEx31dView::DrawDataRow(CDC* pDC, int y)
{
    int x = 0;
    CString strTime, str;
    CEx31dDoc* pDoc = GetDocument();
    for (int i = 0; i < pDoc->m_nFields; i++) {
        CDBVariant var; // must declare this inside the loop
        m_pSet->GetFieldValue(i, var);
        switch (var.m_dwType) {
        case DBVT_STRING:
            str = *var.m_pstring; // narrow characters
            break;
        case DBVT_SHORT:
            str.Format("%d", (int) var.m_iVal);
            break;
        case DBVT_LONG:
            str.Format("%d", var.m_lVal);
            break;
        case DBVT_SINGLE:
            str.Format("%10.2f", (double) var.m_fltVal);
            break;
        case DBVT_DOUBLE:
            str.Format("%10.2f", var.m_dblVal);
            break;
        case DBVT_DATE:
            str.Format("%d/%d/%d", var.m_pdate->month, var.m_pdate->day,
                var.m_pdate->year);
            break;
        case DBVT_BOOL:
            str = (var.m_boolVal == 0) ? "FALSE" : "TRUE";
            break;
        case DBVT_NULL:
            str =  "——";
            break;
        default:
            str.Format("Unk type %d\n", var.m_dwType);
            TRACE("Unknown type %d\n", var.m_dwType);
        }
        pDC->TextOut(x, y, str);
        x += pDoc->m_arrayFieldSize[i] * m_nCharWidth;
    }
}

The code above is excerpted from a sample program EX31D, which is on the CD-ROM included with this book. That program uses the CRowView code from the DAO example, EX32A, described in the next chapter. The programs EX31D and EX32A are similar in architecture and function. EX31D uses ODBC, and EX32A uses DAO.

Although MFC gives you the CRecordset functions GetODBCFieldCount and GetODBCFieldInfo to get field lengths and types, you must call the ODBC function SQLTables to get a "table of tables." The CTables class in the EX31D project encapsulates this table.