⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dbcore.cpp

📁 vc6.0完整版
💻 CPP
📖 第 1 页 / 共 5 页
字号:
// 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 + -