📄 dbcore.cpp
字号:
// 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_ROLLBACK));
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;
}
// Screen for errors.
BOOL CDatabase::Check(RETCODE nRetCode) const
{
return CheckHstmt(nRetCode, SQL_NULL_HSTMT);
}
BOOL CDatabase::CheckHstmt(RETCODE nRetCode, HSTMT hstmt) const
{
ASSERT_VALID(this);
UNUSED(hstmt);
switch (nRetCode)
{
case SQL_SUCCESS_WITH_INFO:
#ifdef _DEBUG
if (afxTraceFlags & traceDatabase)
{
CDBException e(nRetCode);
TRACE0("Warning: ODBC Success With Info, ");
e.BuildErrorString((CDatabase*)this, hstmt);
}
#endif // _DEBUG
// Fall through
case SQL_SUCCESS:
case SQL_NO_DATA_FOUND:
return TRUE;
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////
// CDatabase internal functions
//Replace brackets in SQL string with SQL_IDENTIFIER_QUOTE_CHAR
void CDatabase::ReplaceBrackets(LPTSTR lpchSQL)
{
BOOL bInLiteral = FALSE;
LPTSTR lpchNewSQL = lpchSQL;
while (*lpchSQL != '\0')
{
if (*lpchSQL == _afxLiteralSeparator)
{
// Handle escaped literal
if (*_tcsinc(lpchSQL) == _afxLiteralSeparator)
{
*lpchNewSQL = *lpchSQL;
lpchSQL = _tcsinc(lpchSQL);
lpchNewSQL = _tcsinc(lpchNewSQL);
}
else
bInLiteral = !bInLiteral;
*lpchNewSQL = *lpchSQL;
}
else if (!bInLiteral && (*lpchSQL == '['))
{
if (*_tcsinc(lpchSQL) == '[')
{
// Handle escaped left bracket by inserting one '['
*lpchNewSQL = *lpchSQL;
lpchSQL = _tcsinc(lpchSQL);
}
else
*lpchNewSQL = m_chIDQuoteChar;
}
else if (!bInLiteral && (*lpchSQL == ']'))
{
if (*_tcsinc(lpchSQL) == ']')
{
// Handle escaped right bracket by inserting one ']'
*lpchNewSQL = *lpchSQL;
lpchSQL = _tcsinc(lpchSQL);
}
else
*lpchNewSQL = m_chIDQuoteChar;
}
else
*lpchNewSQL = *lpchSQL;
lpchSQL = _tcsinc(lpchSQL);
lpchNewSQL = _tcsinc(lpchNewSQL);
}
}
// Allocate an henv (first time called) and hdbc
void CDatabase::AllocConnect(DWORD dwOptions)
{
ASSERT_VALID(this);
if (m_hdbc != SQL_NULL_HDBC)
return;
_AFX_DB_STATE* pDbState = _afxDbState;
RETCODE nRetCode;
AfxLockGlobals(CRIT_ODBC);
if (pDbState->m_henvAllConnections == SQL_NULL_HENV)
{
ASSERT(pDbState->m_nAllocatedConnections == 0);
// need to allocate an environment for first connection
AFX_SQL_SYNC(::SQLAllocEnv(&pDbState->m_henvAllConnections));
if (!Check(nRetCode))
{
AfxUnlockGlobals(CRIT_ODBC);
AfxThrowMemoryException(); // fatal
}
}
ASSERT(pDbState->m_henvAllConnections != SQL_NULL_HENV);
AFX_SQL_SYNC(::SQLAllocConnect(pDbState->m_henvAllConnections, &m_hdbc));
if (!Check(nRetCode))
{
AfxUnlockGlobals(CRIT_ODBC);
ThrowDBException(nRetCode); // fatal
}
pDbState->m_nAllocatedConnections++; // allocated at least
AfxUnlockGlobals(CRIT_ODBC);
#ifdef _DEBUG
if (bTraceSql)
{
::SQLSetConnectOption(m_hdbc, SQL_OPT_TRACEFILE,
(DWORD)"odbccall.txt");
::SQLSetConnectOption(m_hdbc, SQL_OPT_TRACE, 1);
}
#endif // _DEBUG
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_LOGIN_TIMEOUT,
m_dwLoginTimeout));
#ifdef _DEBUG
if (nRetCode != SQL_SUCCESS && nRetCode != SQL_SUCCESS_WITH_INFO &&
(afxTraceFlags & traceDatabase))
TRACE0("Warning: Failure setting login timeout.\n");
#endif
if (!m_bUpdatable)
{
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc, SQL_ACCESS_MODE,
SQL_MODE_READ_ONLY));
#ifdef _DEBUG
if (nRetCode != SQL_SUCCESS && nRetCode != SQL_SUCCESS_WITH_INFO &&
(afxTraceFlags & traceDatabase))
TRACE0("Warning: Failure setting read only access mode.\n");
#endif
}
// Turn on cursor lib support
if (dwOptions & useCursorLib)
{
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc,
SQL_ODBC_CURSORS, SQL_CUR_USE_ODBC));
// With cursor library added records immediately in result set
m_bIncRecordCountOnAdd = TRUE;
}
}
BOOL CDatabase::Connect(DWORD dwOptions)
{
USES_CONVERSION;
HWND hWndTop;
HWND hWnd = CWnd::GetSafeOwner_(NULL, &hWndTop);
if (hWnd == NULL)
hWnd = ::GetDesktopWindow();
UCHAR szConnectOutput[MAX_CONNECT_LEN];
RETCODE nRetCode;
SWORD nResult;
UWORD wConnectOption = SQL_DRIVER_COMPLETE;
if (dwOptions & noOdbcDialog)
wConnectOption = SQL_DRIVER_NOPROMPT;
else if (dwOptions & forceOdbcDialog)
wConnectOption = SQL_DRIVER_PROMPT;
AFX_SQL_SYNC(::SQLDriverConnect(m_hdbc, hWnd,
(UCHAR*)T2A((LPTSTR)(LPCTSTR)m_strConnect), SQL_NTS,
szConnectOutput, _countof(szConnectOutput),
&nResult, wConnectOption));
if (hWndTop != NULL)
::EnableWindow(hWndTop, TRUE);
// If user hit 'Cancel'
if (nRetCode == SQL_NO_DATA_FOUND)
{
Free();
return FALSE;
}
if (!Check(nRetCode))
{
#ifdef _DEBUG
if (hWnd == NULL)
TRACE0("Error: No default window (AfxGetApp()->m_pMainWnd) for SQLDriverConnect.\n");
#endif
ThrowDBException(nRetCode);
}
// Connect strings must have "ODBC;"
m_strConnect = _afxODBCTrail;
// Save connect string returned from ODBC
m_strConnect += (char*)szConnectOutput;
return TRUE;
}
void CDatabase::VerifyConnect()
{
RETCODE nRetCode;
SWORD nResult;
SWORD nAPIConformance;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_API_CONFORMANCE,
&nAPIConformance, sizeof(nAPIConformance), &nResult));
if (!Check(nRetCode))
ThrowDBException(nRetCode);
if (nAPIConformance < SQL_OAC_LEVEL1)
ThrowDBException(AFX_SQL_ERROR_API_CONFORMANCE);
SWORD nSQLConformance;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_ODBC_SQL_CONFORMANCE,
&nSQLConformance, sizeof(nSQLConformance), &nResult));
if (!Check(nRetCode))
ThrowDBException(nRetCode);
if (nSQLConformance < SQL_OSC_MINIMUM)
ThrowDBException(AFX_SQL_ERROR_SQL_CONFORMANCE);
}
void CDatabase::GetConnectInfo()
{
RETCODE nRetCode;
SWORD nResult;
// Reset the database update options
m_dwUpdateOptions = 0;
// Check for SQLSetPos support
UDWORD dwDriverPosOperations;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_POS_OPERATIONS,
&dwDriverPosOperations, sizeof(dwDriverPosOperations), &nResult));
if (Check(nRetCode) &&
(dwDriverPosOperations & SQL_POS_UPDATE) &&
(dwDriverPosOperations & SQL_POS_DELETE) &&
(dwDriverPosOperations & SQL_POS_ADD))
m_dwUpdateOptions |= AFX_SQL_SETPOSUPDATES;
// Check for positioned update SQL support
UDWORD dwPositionedStatements;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_POSITIONED_STATEMENTS,
&dwPositionedStatements, sizeof(dwPositionedStatements),
&nResult));
if (Check(nRetCode) &&
(dwPositionedStatements & SQL_PS_POSITIONED_DELETE) &&
(dwPositionedStatements & SQL_PS_POSITIONED_UPDATE))
m_dwUpdateOptions |= AFX_SQL_POSITIONEDSQL;
// Check for transaction support
SWORD nTxnCapable;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_TXN_CAPABLE, &nTxnCapable,
sizeof(nTxnCapable), &nResult));
if (Check(nRetCode) && nTxnCapable != SQL_TC_NONE)
m_bTransactions = TRUE;
// Cache the effect of transactions on cursors
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_CURSOR_COMMIT_BEHAVIOR,
&m_nCursorCommitBehavior, sizeof(m_nCursorCommitBehavior),
&nResult));
if (!Check(nRetCode))
m_nCursorCommitBehavior = SQL_ERROR;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR,
&m_nCursorRollbackBehavior, sizeof(m_nCursorRollbackBehavior),
&nResult));
if (!Check(nRetCode))
m_nCursorRollbackBehavior = SQL_ERROR;
// Cache bookmark attributes
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_BOOKMARK_PERSISTENCE,
&m_dwBookmarkAttributes, sizeof(m_dwBookmarkAttributes),
&nResult));
Check(nRetCode);
// Check for SQLGetData support req'd by RFX_LongBinary
UDWORD dwGetDataExtensions;
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_GETDATA_EXTENSIONS,
&dwGetDataExtensions, sizeof(dwGetDataExtensions),
&nResult));
if (!Check(nRetCode))
dwGetDataExtensions = 0;
if (dwGetDataExtensions & SQL_GD_BOUND)
m_dwUpdateOptions |= AFX_SQL_GDBOUND;
if (m_bUpdatable)
{
// Make sure data source is Updatable
char szReadOnly[10];
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DATA_SOURCE_READ_ONLY,
szReadOnly, _countof(szReadOnly), &nResult));
if (Check(nRetCode) && nResult == 1)
m_bUpdatable = !(lstrcmpA(szReadOnly, "Y") == 0);
else
m_bUpdatable = FALSE;
#ifdef _DEBUG
if (!m_bUpdatable && (afxTraceFlags & traceDatabase))
TRACE0("Warning: data source is readonly.\n");
#endif
}
else
{
// Make data source is !Updatable
AFX_SQL_SYNC(::SQLSetConnectOption(m_hdbc,
SQL_ACCESS_MODE, SQL_MODE_READ_ONLY));
}
// Cache the quote char to use when constructing SQL
char szIDQuoteChar[2];
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_IDENTIFIER_QUOTE_CHAR,
szIDQuoteChar, _countof(szIDQuoteChar), &nResult));
if (Check(nRetCode) && nResult == 1)
m_chIDQuoteChar = szIDQuoteChar[0];
else
m_chIDQuoteChar = ' ';
#ifdef _DEBUG
if (afxTraceFlags & traceDatabase)
{
char szInfo[64];
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DBMS_NAME,
szInfo, _countof(szInfo), &nResult));
if (Check(nRetCode))
{
CString strInfo = szInfo;
TRACE1("DBMS: %s\n", strInfo);
AFX_SQL_SYNC(::SQLGetInfo(m_hdbc, SQL_DBMS_VER,
szInfo, _countof(szInfo), &nResult));
if (Check(nRetCode))
{
strInfo = szInfo;
TRACE1(", Version: %s\n", strInfo);
}
}
}
#endif // _DEBUG
}
void CDatabase::BindParameters(HSTMT /* hstmt */)
{
// Must override and call SQLBindParameter directly
}
//////////////////////////////////////////////////////////////////////////////
// CDatabase diagnostics
#ifdef _DEBUG
void CDatabase::AssertValid() const
{
CObject::AssertValid();
}
void CDatabase::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << "m_hdbc = " << m_hdbc;
dc << "\nm_strConnect = " << m_strConnect;
dc << "\nm_bUpdatable = " << m_bUpdatable;
dc << "\nm_bTransactions = " << m_bTransactions;
dc << "\nm_bTransactionPending = " << m_bTransactionPending;
dc << "\nm_dwLoginTimeout = " << m_dwLoginTimeout;
dc << "\nm_dwQueryTimeout = " << m_dwQueryTimeout;
if (dc.GetDepth() > 0)
{
_AFX_DB_STATE* pDbState = _afxDbState;
dc << "\nwith env:";
dc << "\n\tnAllocated = " << pDbState->m_nAllocatedConnections;
dc << "\n\thenvAllConnections = " << pDbState->m_henvAllConnections;
}
dc << "\n";
}
#endif // _DEBUG
//////////////////////////////////////////////////////////////////////////////
// CRecordset helpers
void AFXAPI AfxSetCurrentRecord(long* plCurrentRecord, long nRows, RETCODE nRetCode);
void AFXAPI AfxSetRecordCount(long* plRecordCount, long lCurrentRecord,
BOOL bEOFSeen, RETCODE nRetCode);
//////////////////////////////////////////////////////////////////////////////
// CRecordset
CRecordset::CRecordset(CDatabase* pDatabase)
{
ASSERT(pDatabase == NULL || AfxIsValidAddress(pDatabase, sizeof(CDatabase)));
m_pDatabase = pDatabase;
m_nOpenType = snapshot;
m_lOpen = AFX_RECORDSET_STATUS_UNKNOWN;
m_nEditMode = noMode;
m_nDefaultType = snapshot;
m_dwOptions = none;
m_bAppendable = FALSE;
m_bUpdatable = FALSE;
m_bScrollable = FALSE;
m_bRecordsetDb = FALSE;
m_bRebindParams = FALSE;
m_bLongBinaryColumns = FALSE;
m_nLockMode = optimistic;
m_dwInitialGetDataLen = 0;
m_rgODBCFieldInfos = NULL;
m_rgFieldInfos = NULL;
m_rgRowStatus = NULL;
m_dwRowsetSize = 25;
m_dwAllocatedRowsetSize = 0;
m_nFields = 0;
m_nParams = 0;
m_nFieldsBound = 0;
m_lCurrentRecord = AFX_CURRENT_RECORD_UNDEFINED;
m_lRecordCount = 0;
m_bUseUpdateSQL = FALSE;
m_bUseODBCCursorLib = FALSE;
m_nResultCols = -1;
m_bCheckCacheForDirtyFields = TRUE;
m_pbFieldFlags = NULL;
m_pbParamFlags = NULL;
m_plParamLength = NULL;
m_pvFieldProxy = NULL;
m_pvParamProxy = NULL;
m_nProxyFields = 0;
m_nProxyParams = 0;
m_hstmtUpdate = SQL_NULL_HSTMT;
m_hstmt = SQL_NULL_HSTMT;
if (m_pDatabase != NULL && m_pDatabase->IsOpen())
{
ASSERT_VALID(m_pDatabase);
TRY
{
RETCODE nRetCode;
AFX_SQL_SYNC(::SQLAllocStmt(m_pDatabase->m_hdbc, &m_hstmt));
if (!Check(nRetCode))
ThrowDBException(SQL_INVALID_HANDLE);
// Add to list of CRecordsets with alloced hstmts
AfxLockGlobals(CRIT_ODBC);
TRY
{
m_pDatabase->m_listRecordsets.AddHead(this);
}
CATCH_ALL(e)
{
AfxUnlockGlobals(CRIT_ODBC);
THROW_LAST();
}
END_CATCH_ALL
AfxUnlockGlobals(CRIT_ODBC);
}
CATCH_ALL(e)
{
ASSERT(m_hstmt == SQL_NULL_HSTMT);
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
}
}
CRecordset::~CRecordset()
{
ASSERT_VALID(this);
TRY
{
if (m_hstmt != NULL)
{
#ifdef _DEBUG
if (m_dwOptions & useMultiRowFetch && afxTraceFlags & traceDatabase)
{
TRACE0("\nWARNING: Close called implicitly from destructor.");
TRACE0("\nUse of multi row fetch requires explicit call");
TRACE0("\nto Close or memory leaks will result.\n");
}
#endif
Close();
}
if (m_bRecordsetDb)
delete m_pDatabase;
m_pDatabase = NULL;
}
CATCH_ALL(e)
{
// Nothing we can do
TRACE0("Error: Exception ignored in ~CRecordset().\n");
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
}
BOOL CRecordset::Open(UINT nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
{
ASSERT(!IsOpen());
ASSERT_VALID(this);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -