MFC ODBC Classes and Parameterized Pre-Defined QueriesID: Q137814
|
The MFC Encyclopedia article "Recordset: Declaring a Class for a Predefined Query" describes how to invoke a predefined query that takes parameters and returns a result set. The instructions in that article will not work for predefined queries that take parameters and do not return a result set. Some possible error messages you may receive when attempting to do this are:
No columns were bound prior to calling SQLExtendedFetch State:SL009:, NATIVE:0, Origin: [Microsoft][ODBC Cursor Library]
There are two ways that you can use predefined queries that take parameters and don't return a result set with the MFC ODBC classes:Invalid cursor state State:24000, Native:24, Origin:[Microsoft][ODBC Microsoft Access Driver]
If your SQL command returns a result set, it is preferable to use a
CRecordset-derived class, and pass the SQL to the recordset Open() member
function. Using CRecordset is preferable when a result set is returned
because the database classes do most of the work of binding the returned
data to variables in your program. The CRecordset class will also do most
of the work of binding parameters as well.
If you have a predefined query that takes parameters, you have a choice
between using a CRecordset-derived class to take advantage of its support
for binding the parameters or using the CDatabase::m_hdbc member variable
and doing the binding yourself.
BOOL CMyRecordset::Open(.....)
{
.
.
.
// MoveFirst(); <<<< comment this line out!
}
In addition, you should not call any function that assumes a result
set is present.
CMyRecordset rs;
rs.m_Param = someValue // value for parameter
rs.Open(CRecordset::snapshot,"{CALL MyQuery (?)}",
CRecordset::readOnly);
rs.Close();
NOTE: In Visual C++ 4.0 and 4.1, the implementation of CRecordset is
changed and requires four additional steps to be taken to make this first
approach work:
class CMyDatabase : public CDatabase
{
public:
CPtrList& GetRecordsetList() { return m_listRecordsets; }
};
// Add to list of CRecordsets with allocated hstmts
m_pDatabase->m_listRecordsets.AddHead(this);
with this:
// Add to list of CRecordsets with allocated hstmts
CPtrList& listRecordsets =
((CMyDatabase*)m_pDatabase)->GetRecordsetList();
listRecordsets.AddHead(this);
The reason for this step is that MFC 4.0 and 4.1 declare CRecordset to be
a friend of CDatabase that allows it to directly manipulate the protected
m_listRecordsets member. Friendship is not inherited, however, so other
measures must be taken to allow the recordset to add itself to the
database's list of recordsets.
NO_CPP_EXCEPTION(strDefaultConnect.Empty());
static const TCHAR szDriverNotCapable[] = _T("State:S1C00");
CDatabase* pDb;
RETCODE nRetCode;
HSTMT hstmt;
SDWORD cBytes;
SDWORD nParamValue;
// Construct and open the database object
pDb = new CDatabase;
pDb->Open("My_Datasource");
// allocate the hstmt
AFX_SQL_SYNC(::SQLAllocStmt(pDb->m_hdbc,&hstmt));
if (!pDb->Check(nRetCode))
AfxThrowDBException(nRetCode,pDb,hstmt);
// bind the parameter
AFX_SQL_SYNC(::SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,
SQL_C_LONG,SQL_INTEGER,10,0,&nParamValue,4,&cBytes));
if (nRetCode != SQL_SUCCESS)
AfxThrowDBException(nRetCode,pDb,hstmt);
// set the parameter value
nParamValue = 3;
// execute the query
AFX_SQL_ASYNC(pDb,::SQLExecDirect(hstmt,
(UCHAR FAR*)"{CALL MyQuery (?)}",SQL_NTS));
if (nRetCode != SQL_SUCCESS && nRetCode != SQL_SUCCESS_WITH_INFO)
AfxThrowDBException(nRetCode,pDb,hstmt);
// free the hstmt
AFX_SQL_SYNC(::SQLFreeStmt(hstmt,SQL_DROP));
// Close and destruct the database object
pDb->Close();
delete pDb;
MFC Encyclopedia Article: "Recordset: Declaring a Class for a Predefined Query."
Additional query words: kbvc200 kbvc210 kbvc220 kbvc400 kbvc410 kbvc420 kbvc500 kbvc600
Keywords : kbcode kbusage kbdocerr kbDatabase kbMFC kbODBC kbVC
Version : 2.0 2.1 2.2 4.0 4.1 4.2 5.0 6.0
Platform : NT WINDOWS
Issue type :
Last Reviewed: August 8, 1999