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

📄 dbcore.cpp

📁 c语言编程软件vc6.0中文绿色版_vc6.0官方下载
💻 CPP
📖 第 1 页 / 共 5 页
字号:
	// If Update hstmt has been allocated, shut it down also
	if (m_hstmtUpdate != SQL_NULL_HSTMT)
		::SQLCancel(m_hstmtUpdate);
}

CString CRecordset::GetDefaultConnect()
{
	ASSERT_VALID(this);

	return _afxODBCTrail;
}

CString CRecordset::GetDefaultSQL()
{
	ASSERT_VALID(this);

	// Override and add table name or entire SQL SELECT statement
	return _T("");
}

void CRecordset::DoFieldExchange(CFieldExchange* /* pFX */)
{
	ASSERT_VALID(this);

	// Do nothing if dynamically retrieving unbound fields,
	// otherwise override CRecordset and add RFX calls
}

void CRecordset::DoBulkFieldExchange(CFieldExchange* /* pFX */)
{
	ASSERT_VALID(this);

	// To use multi-record data fetching, you must use
	// a derived CRecordset class and call Close explicitly.
	ASSERT(FALSE);
}

void CRecordset::OnSetOptions(HSTMT hstmt)
{
	ASSERT_VALID(this);
	ASSERT(hstmt != SQL_NULL_HSTMT);

	// Inherit options settings from CDatabase
	m_pDatabase->OnSetOptions(hstmt);

	// If fowardOnly recordset and not using SQLExtendedFetch, quit now
	if (m_nOpenType == forwardOnly && !(m_dwOptions & useExtendedFetch))
		return;

	// Turn on bookmark support if necessary
	EnableBookmarks();

	// If using forwardOnly and extended fetch, quit now
	if (m_nOpenType == forwardOnly)
		return;

	// Make sure driver supports extended fetch, ODBC 2.0 and requested cursor type
	VerifyDriverBehavior();
	DWORD dwScrollType = VerifyCursorSupport();

	// Set the update method, concurrency and cursor type
	SetUpdateMethod();
	SetConcurrencyAndCursorType(hstmt, dwScrollType);
}

// Screen for errors.
BOOL CRecordset::Check(RETCODE nRetCode) const
{
	ASSERT_VALID(this);

	switch (nRetCode)
	{
	case SQL_SUCCESS_WITH_INFO:
#ifdef _DEBUG
		if (afxTraceFlags & traceDatabase)
		{
			CDBException e(nRetCode);
			TRACE0("Warning: ODBC Success With Info, ");
			e.BuildErrorString(m_pDatabase, m_hstmt);
		}
#endif

		// Fall through

	case SQL_SUCCESS:
	case SQL_NO_DATA_FOUND:
	case SQL_NEED_DATA:
		return TRUE;
	}

	return FALSE;
}

void CRecordset::PreBindFields()
{
	// Do nothing
}

//////////////////////////////////////////////////////////////////////////////
// CRecordset internal functions

// Cache state information internally in CRecordset
void CRecordset::SetState(int nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
{
	if (nOpenType == AFX_DB_USE_DEFAULT_TYPE)
		m_nOpenType = m_nDefaultType;
	else
		m_nOpenType = nOpenType;

	m_bAppendable = (dwOptions & appendOnly) != 0 ||
		(dwOptions & readOnly) == 0;
	m_bUpdatable = (dwOptions & readOnly) == 0 &&
		(dwOptions & appendOnly) == 0;

	// Can turn off dirty field checking via dwOptions
	if (dwOptions & noDirtyFieldCheck || dwOptions & useMultiRowFetch)
		m_bCheckCacheForDirtyFields = FALSE;

	// Set recordset readOnly if forwardOnly
	if (m_nOpenType == forwardOnly && !(dwOptions & readOnly))
	{
#ifdef _DEBUG
		if (afxTraceFlags & traceDatabase)
			TRACE0("Warning: Setting forwardOnly recordset readOnly.\n");
#endif
		dwOptions |= readOnly;

		// If using multiRowFetch also set useExtendFetch
		if (dwOptions & useMultiRowFetch)
			dwOptions |= useExtendedFetch;
	}

	// Archive info for use in Requery
	m_dwOptions = dwOptions;
	m_strRequerySQL = lpszSQL;
	m_strRequeryFilter = m_strFilter;
	m_strRequerySort = m_strSort;
}

// Allocate the Hstmt and implicitly create and open Database if necessary
BOOL CRecordset::AllocHstmt()
{
	RETCODE nRetCode;
	if (m_hstmt == SQL_NULL_HSTMT)
	{
		CString strDefaultConnect;
		TRY
		{
			if (m_pDatabase == NULL)
			{
				m_pDatabase = new CDatabase();
				m_bRecordsetDb = TRUE;
			}

			strDefaultConnect = GetDefaultConnect();

			// If not already opened, attempt to open
			if (!m_pDatabase->IsOpen())
			{
				BOOL bUseCursorLib = m_bUseODBCCursorLib;

				// If non-readOnly snapshot request must use cursor lib
				if (m_nOpenType == snapshot && !(m_dwOptions & readOnly))
				{
					// This assumes drivers only support readOnly snapshots
					bUseCursorLib = TRUE;
				}

				if (!m_pDatabase->Open(_T(""), FALSE, FALSE,
					strDefaultConnect, bUseCursorLib))
				{
					return FALSE;
				}

				// If snapshot cursor requested and not supported, load cursor lib
				if (m_nOpenType == snapshot && !bUseCursorLib)
				{
					// Get the supported cursor types
					RETCODE nResult;
					UDWORD dwDriverScrollOptions;
					AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_SCROLL_OPTIONS,
						&dwDriverScrollOptions, sizeof(dwDriverScrollOptions), &nResult));
					if (!Check(nRetCode))
					{
						TRACE0("Error: ODBC failure checking for driver capabilities.\n");
						ThrowDBException(nRetCode);
					}

					// Check for STATIC cursor support and load cursor lib
					if (!(dwDriverScrollOptions & SQL_SO_STATIC))
					{
						m_pDatabase->Close();
						if (!m_pDatabase->Open(_T(""), FALSE, FALSE,
							strDefaultConnect, TRUE))
							return FALSE;
					}
				}
			}

			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)
		{
#ifdef _DEBUG
			if (afxTraceFlags & traceDatabase)
				TRACE0("Error: CDatabase create for CRecordset failed.\n");
#endif
			NO_CPP_EXCEPTION(strDefaultConnect.Empty());
			if (m_bRecordsetDb)
			{
				delete m_pDatabase;
				m_pDatabase = NULL;
			}
			ASSERT(m_hstmt == SQL_NULL_HSTMT);
			THROW_LAST();
		}
		END_CATCH_ALL
	}

	return TRUE;
}

// Initialize the status arrays and create the SQL
void CRecordset::BuildSQL(LPCTSTR lpszSQL)
{
	if (lpszSQL == NULL)
		m_strSQL = GetDefaultSQL();
	else
		m_strSQL = lpszSQL;

	// Set any supplied params
	if (m_nParams != 0)
	{
		UINT nParams = BindParams(m_hstmt);
		ASSERT(nParams == m_nParams);
	}

	// Construct the SQL string
	BuildSelectSQL();
	AppendFilterAndSortSQL();

	// Do some extra checking if trying to set recordset updatable or appendable
	if ((m_bUpdatable || m_bAppendable) && !IsRecordsetUpdatable())
		m_bUpdatable = m_bAppendable = FALSE;

	if (m_bUpdatable && m_bUseUpdateSQL && m_pDatabase->m_bAddForUpdate)
		m_strSQL += _afxForUpdate;

	// Replace brackets with SQL_IDENTIFIER_QUOTE_CHAR
	m_pDatabase->ReplaceBrackets(m_strSQL.GetBuffer(0));
	m_strSQL.ReleaseBuffer();
}

// Prepare and Execute the SQL or simple call SQLExecDirect, resetting concurrency if necessary
void CRecordset::PrepareAndExecute()
{
	USES_CONVERSION;
	RETCODE nRetCode = 0;
	BOOL bConcurrency = FALSE;
	LPCSTR lpszWSQL = T2CA(m_strSQL);

	while (!bConcurrency)
	{
		// Prepare or execute the query
		if (m_dwOptions & executeDirect)
		{
			AFX_ODBC_CALL(::SQLExecDirect(m_hstmt,
				(UCHAR*)lpszWSQL, SQL_NTS));
		}
		else
		{
			AFX_ODBC_CALL(::SQLPrepare(m_hstmt,
				(UCHAR*)lpszWSQL, SQL_NTS));
		}
		if (Check(nRetCode))
			bConcurrency = TRUE;
		else
		{
			// If "Driver Not Capable" error, assume cursor type doesn't
			// support requested concurrency and try alternate concurrency.
			CDBException* e = new CDBException(nRetCode);
			e->BuildErrorString(m_pDatabase, m_hstmt);
			if (m_dwConcurrency != SQL_CONCUR_READ_ONLY &&
				e->m_strStateNativeOrigin.Find(_afxDriverNotCapable) >= 0)
			{
#ifdef _DEBUG
				if (afxTraceFlags & traceDatabase)
					TRACE0("Warning: Driver does not support requested concurrency.\n");
#endif

				// Don't need exception to persist while attempting to reset concurrency
				e->Delete();

				// ODBC will automatically attempt to set alternate concurrency if
				// request fails, but it won't try LOCK even if driver supports it.
				if ((m_dwDriverConcurrency & SQL_SCCO_LOCK) &&
					(m_dwConcurrency == SQL_CONCUR_ROWVER ||
					m_dwConcurrency == SQL_CONCUR_VALUES))
				{
					m_dwConcurrency = SQL_CONCUR_LOCK;
				}
				else
				{
					m_dwConcurrency = SQL_CONCUR_READ_ONLY;
					m_bUpdatable = m_bAppendable = FALSE;
#ifdef _DEBUG
					if (afxTraceFlags & traceDatabase)
						TRACE0("Warning: Setting recordset read only.\n");
#endif
				}

				// Attempt to reset the concurrency model.
				AFX_SQL_SYNC(::SQLSetStmtOption(m_hstmt, SQL_CONCURRENCY,
					m_dwConcurrency));
				if (!Check(nRetCode))
				{
					TRACE0("Error: ODBC failure setting recordset concurrency.\n");
					ThrowDBException(nRetCode);
				}
			}
			else
			{
				TRACE0("Error: ODBC failure on SQLPrepare or SQLExecDirect\n");
				THROW(e);
			}
		}
	}


	// now attempt to execute the SQL Query if not executed already
	if (!(m_dwOptions & executeDirect))
	{
		AFX_ODBC_CALL(::SQLExecute(m_hstmt));
		if (!Check(nRetCode))
			ThrowDBException(nRetCode);
	}
	m_lOpen = AFX_RECORDSET_STATUS_OPEN;

	// SQLExecute or SQLExecDirect may have changed an option value
	if (nRetCode == SQL_SUCCESS_WITH_INFO)
	{
		// Check if concurrency was changed in order to mark
		// recordset non-updatable if necessary
		DWORD dwConcurrency;
		AFX_SQL_SYNC(::SQLGetStmtOption(m_hstmt, SQL_CONCURRENCY, &dwConcurrency));
		if (!Check(nRetCode))
			ThrowDBException(nRetCode);

		if (dwConcurrency == SQL_CONCUR_READ_ONLY && (m_bUpdatable || m_bAppendable))
		{
			m_bUpdatable = FALSE;
			m_bAppendable = FALSE;

#ifdef _DEBUG
			if (afxTraceFlags & traceDatabase)
			{
				TRACE0("Warning: Concurrency changed by driver.\n");
				TRACE0("\tMarking CRecordset as not updatable.\n");
			}
#endif // _DEBUG
		}
	}
}

// Ensure that driver supports extended fetch and ODBC 2.0 if necessary
void CRecordset::VerifyDriverBehavior()
{
	RETCODE nRetCode;
	UWORD wScrollable;
	// If SQLExtendedFetch not supported, use SQLFetch
	AFX_SQL_SYNC(::SQLGetFunctions(m_pDatabase->m_hdbc,
		SQL_API_SQLEXTENDEDFETCH, &wScrollable));
	if (!Check(nRetCode))
	{
		TRACE0("Error: ODBC failure determining whether recordset is scrollable.\n");
		ThrowDBException(nRetCode);
	}
	m_bScrollable = wScrollable;
	if (!m_bScrollable)
	{
#ifdef _DEBUG
		if (afxTraceFlags & traceDatabase)
		{
			TRACE0("Warning: SQLExtendedFetch not supported by driver\n");
			TRACE0("and/or cursor library not loaded. Opening forwardOnly.\n");
			TRACE0("for use with SQLFetch.\n");
		}
#endif
		m_bUpdatable = FALSE;
		return;
	}

	char szResult[30];
	SWORD nResult;
	// require ODBC v2.0
	AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_ODBC_VER,
		&szResult, _countof(szResult), &nResult));
	if (!Check(nRetCode))
	{
		TRACE0("Error: ODBC failure checking for driver capabilities.\n");
		ThrowDBException(nRetCode);
	}
	if (szResult[0] == '0' && szResult[1] < '2')
		ThrowDBException(AFX_SQL_ERROR_ODBC_V2_REQUIRED);
}

// Check that driver supports requested cursor type
DWORD CRecordset::VerifyCursorSupport()
{
	RETCODE nRetCode;
	SWORD nResult;
	UDWORD dwDriverScrollOptions;
	AFX_SQL_SYNC(::SQLGetInfo(m_pDatabase->m_hdbc, SQL_SCROLL_OPTIONS,
		&dwDriverScrollOptions, sizeof(dwDriverScrollOptions), &nResult));
	if (!Check(nRetCode))
	{
		TRACE0("Error: ODBC failure checking for driver capabilities.\n");
		ThrowDBException(nRetCode);
	}

	SDWORD dwScrollOptions = SQL_CURSOR_FORWARD_ONLY;
	if (m_nOpenType == dynaset)
	{
		// Dynaset support requires ODBC's keyset driven cursor model
		if (!(dwDriverScrollOptions & SQL_SO_KEYSET_DRIVEN))
			ThrowDBException(AFX_SQL_ERROR_DYNASET_NOT_SUPPORTED);
		dwScrollOptions = SQL_CURSOR_KEYSET_DRIVEN;
	}
	else if (m_nOpenType == snapshot)
	{
		// Snapshot support requires ODBC's static cursor model
		if (!(dwDriverScrollOptions & SQL_SO_STATIC))
			ThrowDBException(AFX_SQL_ERROR_SNAPSHOT_NOT_SUPPORTED);
		dwScrollOptions = SQL_CURSOR_STATIC;
	}
	else
	{
		// Dynamic cursor support requires ODBC's dynamic cursor model
		if (!(dwDriverScrollOptions & SQL_SO_DYNAMIC))
			ThrowDBException(AFX_SQL_ERROR_DYNAMIC_CURSOR_NOT_SUPPORTED);
		dwScrollOptions = SQL_CURSOR_DYNAMIC;
	}

	return dwScrollOptions;
}

void CRecordset::AllocAndCacheFieldInfo()
{
	ASSERT(GetODBCFieldCount() < 0);
	ASSERT(m_rgODBCFieldInfos == NULL);

	RETCODE nRetCode;
	SWORD nActualLen;

	// Cache the number of result columns
	AFX_ODBC_CALL(::SQLNumResultCols(m_hstmt, &m_nResultCols));
	if (!Check(nRetCode))
	{
		TRACE0("Error: Can't get field info.\n");
		ThrowDBException(nRetCode);
	}

	// If there are no fields quit now
	if (m_nResultCols == 0)
		return;

	// Allocate buffer and get the ODBC meta data
	m_rgODBCFieldInfos = new CODBCFieldInfo[m_nResultCols];
	LPSTR lpszFieldName;

#ifdef _UNICODE
	// Need proxy to temporarily store non-UNICODE name
	lpszFieldName = new char[MAX_FNAME_LEN + 1];
#endif

	// Get the field info each field
	for (WORD n = 1; n <= GetODBCFieldCount(); n++)
	{
#ifndef _UNICODE
		// Reset the buffer to point to next element
		lpszFieldName =
			m_rgODBCFieldInfos[n - 1].m_strName.GetBuffer(MAX_FNAME_LEN + 1);
#endif

		AFX_ODBC_CALL(::SQLDescribeCol(m_hstmt, n,
			(UCHAR*)lpszFieldName, MAX_FNAME_LEN, &nActualLen,
			&m_rgODBCFieldInfos[n - 1].m_nSQLType,
			&m_rgODBCFieldInfos[n - 1].m_nPrecision,
			&m_rgODBCFieldInfos[n - 1].m_nScale,
			&m_rgODBCFieldInfos[n - 1].m_nNullability));

#ifndef _UNICODE
		m_rgODBCFieldInfos[n - 1].m_strName.ReleaseBuffer(nActualLen);
#else
		// Copy the proxy data to correct element
		m_rgODBCFieldInfos[n - 1].m_strName = lpszFieldName;
#endif

		if (!Check(nRetCode))
		{
			TRACE1("Error: ODBC failure getting field #%d info.\n", n);
			ThrowDBException(nRetCode);
		}
	}

#ifdef _UNICODE
	delete[] lpszFieldName;
#endif
}

void CRecordset::AllocRowset()
{
	if (m_dwOptions & useMultiRowFetch)
		SetRowsetSize(m_dwRowsetSize);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -