Exceptions: Database Exceptions

This article explains how to handle database exceptions. Most of the material in this article applies whether you are working with the MFC classes for Open Database Connectivity (ODBC) or the MFC classes for Data Access Objects (DAO). Material specific to one or the other model is explicitly marked. Topics include:

  • Approaches to exception handling

  • A database exception-handling example

Approaches to Exception Handling

The approach is the same whether you are working with DAO or ODBC.

You should always write exception handlers to handle exceptional conditions.

The most pragmatic approach to catching database exceptions is to test your application with exception scenarios. Determine the likely exceptions that might occur for an operation in your code, and force the exception to occur. Then examine the trace output to see what exception is thrown, or examine the returned error information in the debugger. This lets you know which return codes you'll see for the exception scenarios you are using.

Error Codes Used for ODBC Exceptions

In addition to return codes defined by the framework, which have names of the form AFX_SQL_ERROR_XXX, some CDBExceptions are based on ODBC return codes. The return codes for such exceptions have names of the form SQL_ERROR_XXX.

The return codes — both framework-defined and ODBC-defined — that the database classes can return are documented under the m_nRetCode data member of class CDBException. Additional information about return codes defined by ODBC is available in the ODBC SDK Programmer's Reference in the MSDN Library.

Error Codes Used for DAO Exceptions

For DAO exceptions, more information is typically available. You can access error information through three data members of a caught CDaoException object:

  • m_pErrorInfo contains a pointer to a CDaoErrorInfo object that encapsulates error information in DAO's collection of error objects associated with the database.

  • m_nAfxDaoError contains an extended error code from the MFC DAO classes. These error codes, which have names of the form AFX_DAO_ERROR_XXX, are documented under the data member in CDaoException.

  • m_scode contains an OLE SCODE from DAO, if applicable. You'll seldom need to work with this error code, however. Usually more information is available in the other two data members. See the data member for more about SCODE values.

Additional information about DAO errors, the DAO Error object type, and the DAO Errors collection is available under class CDaoException.

A Database Exception-Handling Example

The following example attempts to construct a CRecordset-derived object on the heap with the new operator, and then open the recordset (for an ODBC data source). For a similar example for the DAO classes, see "DAO Exception Example" below.

ODBC Exception Example

The Open member function could throw an exception (of type CDBException for the ODBC classes), so this code brackets the Open call with a try block. The subsequent catch block will catch a CDBException. You could examine the exception object itself, called e, but in this case it is enough to know that the attempt to create a recordset has failed. The catch block displays a message box and cleans up by deleting the recordset object.

CRecordset* CMyDatabaseDoc::GetRecordset()
{
   CCourses* pSet = new CCourses(&m_dbCust);
   try
   {
      pSet->Open();
   }
   catch(CDBException* e)
   {
      AfxMessageBox(e->m_strError, MB_ICONEXCLAMATION);
      // Delete the incomplete recordset object 
      delete pSet;
      pSet = NULL;
      e->Delete();
   }
   return pSet;
}

DAO Exception Example

The DAO example is similar to the example for ODBC, but you can typically retrieve more kinds of information. The following code also attempts to open a recordset. If that attempt throws an exception, you can examine a data member of the exception object for error information. As with the previous ODBC example, it is probably enough to know that the attempt to create a recordset failed.

CDaoRecordset* CMyDaoDatabaseDoc::GetRecordset()
{
   CDaoRecordset* pSet = new CCustSet(&m_db);
   try
   {
      pSet->Open();
   }
   catch(CDaoException* pe)
   {
      AfxMessageBox(pe->m_pErrorInfo->m_strDescription, MB_ICONEXCLAMATION);
      // Delete the incomplete recordset object 
      delete pSet;
      pSet = NULL;
      pe->Delete();
   }
   return pSet;
}

This code gets an error message string from the m_pErrorInfo member of the exception object. MFC fills this member when it throws the exception.

For a discussion of the error information returned by a CDaoException object, see classes CDaoException and CDaoErrorInfo.

When you are working with Microsoft Jet (.mdb) databases, and in most cases when you are working with ODBC, there will be only one error object. In the rare case when you are using an ODBC data source and there are multiple errors, you can loop through DAO's Errors collection based on the number of errors returned by CDaoException::GetErrorCount. Each time through the loop, call CDaoException::GetErrorInfo to refill the m_pErrorInfo data member.

See Also

Concepts

Exception Handling in MFC