📄 dbcore.cpp
字号:
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#ifdef AFX_DB_SEG
#pragma code_seg(AFX_DB_SEG)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
/////////////////////////////////////////////////////////////////////////////
// Global data
#ifdef _DEBUG
BOOL bTraceSql = FALSE;
#endif
AFX_STATIC_DATA const TCHAR _afxODBCTrail[] = _T("ODBC;");
AFX_STATIC_DATA const TCHAR _afxComma[] = _T(",");
AFX_STATIC_DATA const TCHAR _afxLiteralSeparator = '\'';
AFX_STATIC_DATA const TCHAR _afxCall[] = _T("{CALL ");
AFX_STATIC_DATA const TCHAR _afxParamCall[] = _T("{?");
AFX_STATIC_DATA const TCHAR _afxSelect[] = _T("SELECT ");
AFX_STATIC_DATA const TCHAR _afxFrom[] = _T(" FROM ");
AFX_STATIC_DATA const TCHAR _afxWhere[] = _T(" WHERE ");
AFX_STATIC_DATA const TCHAR _afxOrderBy[] = _T(" ORDER BY ");
AFX_STATIC_DATA const TCHAR _afxForUpdate[] = _T(" FOR UPDATE ");
AFX_STATIC_DATA const TCHAR _afxRowFetch[] = _T("State:01S01");
AFX_STATIC_DATA const TCHAR _afxDataTruncated[] = _T("State:01004");
AFX_STATIC_DATA const TCHAR _afxInfoRange[] = _T("State:S1096");
AFX_STATIC_DATA const TCHAR _afxOutOfSequence[] = _T("State:S1010");
AFX_STATIC_DATA const TCHAR _afxDriverNotCapable[] = _T("State:S1C00");
AFX_STATIC_DATA const char _afxODBCDLL[] = "ODBC32.DLL";
/////////////////////////////////////////////////////////////////////////////
// for dynamic load of ODBC32.DLL
#pragma comment(lib, "odbc32.lib")
/////////////////////////////////////////////////////////////////////////////
// CDBException
void AFXAPI AfxThrowDBException(RETCODE nRetCode, CDatabase* pdb, HSTMT hstmt)
{
CDBException* pException = new CDBException(nRetCode);
if (nRetCode == SQL_ERROR && pdb != NULL)
pException->BuildErrorString(pdb, hstmt);
else if (nRetCode > AFX_SQL_ERROR && nRetCode < AFX_SQL_ERROR_MAX)
{
VERIFY(pException->m_strError.LoadString(
AFX_IDP_SQL_FIRST+(nRetCode-AFX_SQL_ERROR)));
TRACE1("%s\n", pException->m_strError);
}
THROW(pException);
}
CDBException::CDBException(RETCODE nRetCode)
{
m_nRetCode = nRetCode;
}
CDBException::~CDBException()
{
}
void CDBException::BuildErrorString(CDatabase* pdb, HSTMT hstmt, BOOL bTrace)
{
ASSERT_VALID(this);
UNUSED(bTrace); // unused in release builds
if (pdb != NULL)
{
SWORD nOutlen;
RETCODE nRetCode;
UCHAR lpszMsg[SQL_MAX_MESSAGE_LENGTH];
UCHAR lpszState[SQL_SQLSTATE_SIZE];
CString strMsg;
CString strState;
SDWORD lNative;
_AFX_DB_STATE* pDbState = _afxDbState;
AFX_SQL_SYNC(::SQLError(pDbState->m_henvAllConnections, pdb->m_hdbc,
hstmt, lpszState, &lNative,
lpszMsg, SQL_MAX_MESSAGE_LENGTH-1, &nOutlen));
strState = lpszState;
// Skip non-errors
while ((nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO) &&
lstrcmp(strState, _T("00000")) != 0)
{
strMsg = lpszMsg;
TCHAR lpszNative[50];
wsprintf(lpszNative, _T(",Native:%ld,Origin:"), lNative);
strState += lpszNative;
// transfer [origin] from message string to StateNativeOrigin string
int nCloseBracket;
int nMsgLength;
while (!strMsg.IsEmpty() &&
strMsg[0] == '[' && (nCloseBracket = strMsg.Find(']')) >= 0)
{
// Skip ']'
nCloseBracket++;
strState += strMsg.Left(nCloseBracket);
nMsgLength = strMsg.GetLength();
// Skip ' ', if present
if (nCloseBracket < nMsgLength && strMsg[nCloseBracket] == ' ')
nCloseBracket++;
strMsg = strMsg.Right(nMsgLength - nCloseBracket);
}
strState += _T("\n");
m_strStateNativeOrigin += _T("State:") + strState;
m_strError += strMsg + _T("\n");
#ifdef _DEBUG
if (bTrace)
{
TraceErrorMessage(strMsg);
TraceErrorMessage(_T("State:") + strState);
}
#endif // _DEBUG
AFX_SQL_SYNC(::SQLError(pDbState->m_henvAllConnections,
pdb->m_hdbc, hstmt, lpszState, &lNative,
lpszMsg, SQL_MAX_MESSAGE_LENGTH-1, &nOutlen));
strState = lpszState;
}
}
}
BOOL CDBException::GetErrorMessage(LPTSTR lpszError, UINT nMaxError,
PUINT pnHelpContext /* = NULL */)
{
ASSERT(lpszError != NULL && AfxIsValidString(lpszError, nMaxError));
if (pnHelpContext != NULL)
*pnHelpContext = 0;
lstrcpyn(lpszError, m_strError, nMaxError-1);
lpszError[nMaxError-1] = '\0';
return TRUE;
}
#ifdef _DEBUG
void CDBException::TraceErrorMessage(LPCTSTR szTrace) const
{
CString strTrace = szTrace;
if (strTrace.GetLength() <= 80)
TRACE1("%s\n", strTrace);
else
{
// Display 80 chars/line
while (strTrace.GetLength() > 80)
{
TRACE1("%s\n", strTrace.Left(80));
strTrace = strTrace.Right(strTrace.GetLength() - 80);
}
TRACE1("%s\n", strTrace);
}
}
#endif // _DEBUG
void CDBException::Empty()
{
m_strError.Empty();
m_strStateNativeOrigin.Empty();
}
/////////////////////////////////////////////////////////////////////////////
// Global helper
HENV AFXAPI AfxGetHENV()
{
_AFX_DB_STATE* pDbState = _afxDbState;
return pDbState->m_henvAllConnections;
}
/////////////////////////////////////////////////////////////////////////////
// CDatabase implementation
CDatabase::CDatabase()
{
m_hdbc = SQL_NULL_HDBC;
m_hstmt = SQL_NULL_HSTMT;
m_bUpdatable = FALSE;
m_bTransactions = FALSE;
DEBUG_ONLY(m_bTransactionPending = FALSE);
m_dwLoginTimeout = DEFAULT_LOGIN_TIMEOUT;
m_dwQueryTimeout = DEFAULT_QUERY_TIMEOUT;
m_bStripTrailingSpaces = FALSE;
m_bIncRecordCountOnAdd = FALSE;
m_bAddForUpdate = FALSE;
}
CDatabase::~CDatabase()
{
ASSERT_VALID(this);
Free();
}
BOOL CDatabase::Open(LPCTSTR lpszDSN, BOOL bExclusive,
BOOL bReadonly, LPCTSTR lpszConnect, BOOL bUseCursorLib)
{
ASSERT(lpszDSN == NULL || AfxIsValidString(lpszDSN));
ASSERT(lpszConnect == NULL || AfxIsValidString(lpszConnect));
CString strConnect;
if (lpszConnect != NULL)
strConnect = lpszConnect;
// For VB/Access compatibility, require "ODBC;" (or "odbc;")
// prefix to the connect string
if (_tcsnicmp(strConnect, _afxODBCTrail, lstrlen(_afxODBCTrail)) != 0)
{
TRACE0("Error: Missing 'ODBC' prefix on connect string.\n");
return FALSE;
}
// Strip "ODBC;"
strConnect = strConnect.Right(strConnect.GetLength()
- lstrlen(_afxODBCTrail));
if (lpszDSN != NULL && lstrlen(lpszDSN) != 0)
{
// Append "DSN=" lpszDSN
strConnect += _T(";DSN=");
strConnect += lpszDSN;
}
DWORD dwOptions = 0;
if (bExclusive)
dwOptions |= openExclusive;
if (bReadonly)
dwOptions |= openReadOnly;
if (bUseCursorLib)
dwOptions |= useCursorLib;
return OpenEx(strConnect, dwOptions);
}
BOOL CDatabase::OpenEx(LPCTSTR lpszConnectString, DWORD dwOptions)
{
ASSERT_VALID(this);
ASSERT(lpszConnectString == NULL || AfxIsValidString(lpszConnectString));
ASSERT(!(dwOptions & noOdbcDialog && dwOptions & forceOdbcDialog));
// Exclusive access not supported.
ASSERT(!(dwOptions & openExclusive));
m_bUpdatable = !(dwOptions & openReadOnly);
TRY
{
m_strConnect = lpszConnectString;
// Allocate the HDBC and make connection
AllocConnect(dwOptions);
if(!Connect(dwOptions))
return FALSE;
// Verify support for required functionality and cache info
VerifyConnect();
GetConnectInfo();
}
CATCH_ALL(e)
{
Free();
THROW_LAST();
}
END_CATCH_ALL
return TRUE;
}
void CDatabase::ExecuteSQL(LPCTSTR lpszSQL)
{
USES_CONVERSION;
RETCODE nRetCode;
HSTMT hstmt;
ASSERT_VALID(this);
ASSERT(AfxIsValidString(lpszSQL));
AFX_SQL_SYNC(::SQLAllocStmt(m_hdbc, &hstmt));
if (!CheckHstmt(nRetCode, hstmt))
AfxThrowDBException(nRetCode, this, hstmt);
TRY
{
OnSetOptions(hstmt);
// Give derived CDatabase classes option to use parameters
BindParameters(hstmt);
AFX_ODBC_CALL(::SQLExecDirect(hstmt,
(UCHAR*)T2A((LPTSTR)lpszSQL), SQL_NTS));
if (!CheckHstmt(nRetCode, hstmt))
AfxThrowDBException(nRetCode, this, hstmt);
else
{
do
{
SWORD nResultColumns;
AFX_ODBC_CALL(::SQLNumResultCols(hstmt, &nResultColumns));
if (nResultColumns != 0)
{
do
{
AFX_ODBC_CALL(::SQLFetch(hstmt));
} while (CheckHstmt(nRetCode, hstmt) &&
nRetCode != SQL_NO_DATA_FOUND);
}
AFX_ODBC_CALL(::SQLMoreResults(hstmt));
} while (CheckHstmt(nRetCode, hstmt) &&
nRetCode != SQL_NO_DATA_FOUND);
}
}
CATCH_ALL(e)
{
::SQLCancel(hstmt);
AFX_SQL_SYNC(::SQLFreeStmt(hstmt, SQL_DROP));
THROW_LAST();
}
END_CATCH_ALL
AFX_SQL_SYNC(::SQLFreeStmt(hstmt, SQL_DROP));
}
// Shutdown pending query for CDatabase's private m_hstmt
void CDatabase::Cancel()
{
ASSERT_VALID(this);
ASSERT(m_hdbc != SQL_NULL_HDBC);
::SQLCancel(m_hstmt);
}
// Disconnect connection
void CDatabase::Close()
{
ASSERT_VALID(this);
// Close any open recordsets
AfxLockGlobals(CRIT_ODBC);
TRY
{
while (!m_listRecordsets.IsEmpty())
{
CRecordset* pSet = (CRecordset*)m_listRecordsets.GetHead();
pSet->Close(); // will implicitly remove from list
pSet->m_pDatabase = NULL;
}
}
CATCH_ALL(e)
{
AfxUnlockGlobals(CRIT_ODBC);
THROW_LAST();
}
END_CATCH_ALL
AfxUnlockGlobals(CRIT_ODBC);
if (m_hdbc != SQL_NULL_HDBC)
{
RETCODE nRetCode;
AFX_SQL_SYNC(::SQLDisconnect(m_hdbc));
AFX_SQL_SYNC(::SQLFreeConnect(m_hdbc));
m_hdbc = SQL_NULL_HDBC;
_AFX_DB_STATE* pDbState = _afxDbState;
AfxLockGlobals(CRIT_ODBC);
ASSERT(pDbState->m_nAllocatedConnections != 0);
pDbState->m_nAllocatedConnections--;
AfxUnlockGlobals(CRIT_ODBC);
}
}
// Silently disconnect and free all ODBC resources. Don't throw any exceptions
void CDatabase::Free()
{
ASSERT_VALID(this);
// Trap failures upon close
TRY
{
Close();
}
CATCH_ALL(e)
{
// Nothing we can do
TRACE0("Error: exception by CDatabase::Close() ignored in CDatabase::Free().\n");
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
// free henv if refcount goes to 0
_AFX_DB_STATE* pDbState = _afxDbState;
AfxLockGlobals(CRIT_ODBC);
if (pDbState->m_henvAllConnections != SQL_NULL_HENV)
{
ASSERT(pDbState->m_nAllocatedConnections >= 0);
if (pDbState->m_nAllocatedConnections == 0)
{
// free last connection - release HENV
RETCODE nRetCodeEnv = ::SQLFreeEnv(pDbState->m_henvAllConnections);
#ifdef _DEBUG
if (nRetCodeEnv != SQL_SUCCESS)
// Nothing we can do
TRACE0("Error: SQLFreeEnv failure ignored in CDatabase::Free().\n");
#endif
pDbState->m_henvAllConnections = SQL_NULL_HENV;
}
}
AfxUnlockGlobals(CRIT_ODBC);
}
void CDatabase::OnSetOptions(HSTMT hstmt)
{
RETCODE nRetCode;
ASSERT_VALID(this);
ASSERT(m_hdbc != SQL_NULL_HDBC);
if (m_dwQueryTimeout != -1)
{
// Attempt to set query timeout. Ignore failure
AFX_SQL_SYNC(::SQLSetStmtOption(hstmt, SQL_QUERY_TIMEOUT,
m_dwQueryTimeout));
if (!Check(nRetCode))
// don't attempt it again
m_dwQueryTimeout = (DWORD)-1;
}
}
CString CDatabase::GetDatabaseName() const
{
ASSERT_VALID(this);
ASSERT(m_hdbc != SQL_NULL_HDBC);
char szName[MAX_TNAME_LEN];
SWORD nResult;
RETCODE nRetCode;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DATABASE_NAME,
szName, _countof(szName), &nResult));
if (!Check(nRetCode))
szName[0] = '\0';
return szName;
}
BOOL CDatabase::BeginTrans()
{
ASSERT_VALID(this);
ASSERT(m_hdbc != SQL_NULL_HDBC);
if (!m_bTransactions)
return FALSE;
// Only 1 level of transactions supported
#ifdef _DEBUG
ASSERT(!m_bTransactionPending);
#endif
RETCODE nRetCode;
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_AUTOCOMMIT,
SQL_AUTOCOMMIT_OFF));
DEBUG_ONLY(m_bTransactionPending = TRUE);
return Check(nRetCode);
}
BOOL CDatabase::CommitTrans()
{
ASSERT_VALID(this);
ASSERT(m_hdbc != SQL_NULL_HDBC);
if (!m_bTransactions)
return FALSE;
// BeginTrans must be called first
#ifdef _DEBUG
ASSERT(m_bTransactionPending);
#endif
_AFX_DB_STATE* pDbState = _afxDbState;
RETCODE nRetCode;
AFX_SQL_SYNC(::SQLTransact(pDbState->m_henvAllConnections, m_hdbc, SQL_COMMIT));
BOOL bSuccess = Check(nRetCode);
// Turn back on auto commit
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_AUTOCOMMIT,
SQL_AUTOCOMMIT_ON));
DEBUG_ONLY(m_bTransactionPending = FALSE);
return bSuccess;
}
BOOL CDatabase::Rollback()
{
ASSERT_VALID(this);
ASSERT(m_hdbc != SQL_NULL_HDBC);
if (!m_bTransactions)
return FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -