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

📄 dbcore.cpp

📁 c语言编程软件vc6.0中文绿色版_vc6.0官方下载
💻 CPP
📖 第 1 页 / 共 5 页
字号:
	ASSERT(lpszSQL == NULL || AfxIsValidString(lpszSQL));
	ASSERT(nOpenType == AFX_DB_USE_DEFAULT_TYPE ||
		nOpenType == dynaset || nOpenType == snapshot ||
		nOpenType == forwardOnly || nOpenType == dynamic);
	ASSERT(!(dwOptions & readOnly && dwOptions & appendOnly));

	// Can only use optimizeBulkAdd with appendOnly recordsets
	ASSERT((dwOptions & optimizeBulkAdd && dwOptions & appendOnly) ||
		!(dwOptions & optimizeBulkAdd));

	// forwardOnly recordsets have limited functionality
	ASSERT(!(nOpenType == forwardOnly && dwOptions & skipDeletedRecords));

	// Cache state info and allocate hstmt
	SetState(nOpenType, lpszSQL, dwOptions);
	if(!AllocHstmt())
		return FALSE;

	// Check if bookmarks upported (CanBookmark depends on open DB)
	ASSERT(dwOptions & useBookmarks ? CanBookmark() : TRUE);

	TRY
	{
		OnSetOptions(m_hstmt);

		// Allocate the field/param status arrays, if necessary
		BOOL bUnbound = FALSE;
		if (m_nFields > 0 || m_nParams > 0)
			AllocStatusArrays();
		else
			bUnbound = TRUE;

		// Build SQL and prep/execute or just execute direct
		BuildSQL(lpszSQL);
		PrepareAndExecute();

		// Cache some field info and prepare the rowset
		AllocAndCacheFieldInfo();
		AllocRowset();

		// If late binding, still need to allocate status arrays
		if (bUnbound && (m_nFields > 0 || m_nParams > 0))
			AllocStatusArrays();

		// Give derived classes a call before binding
		PreBindFields();

		// Fetch the first row of data
		MoveNext();

		// If EOF, then result set empty, so set BOF as well
		m_bBOF = m_bEOF;
	}
	CATCH_ALL(e)
	{
		Close();
		THROW_LAST();
	}
	END_CATCH_ALL

	return TRUE;
}

void CRecordset::Close()
{
	ASSERT_VALID(this);
	// Can't close if database has been deleted
	ASSERT(m_pDatabase != NULL);

	// This will force a requery for cursor name if reopened.
	m_strCursorName.Empty();

	if (m_rgFieldInfos != NULL &&
		m_nFields > 0 && m_bCheckCacheForDirtyFields)
	{
		FreeDataCache();
	}

	FreeRowset();

	m_nEditMode = noMode;

	delete [] m_rgFieldInfos;
	m_rgFieldInfos = NULL;

	delete [] m_rgODBCFieldInfos;
	m_rgODBCFieldInfos = NULL;

	delete [] m_pbFieldFlags;
	m_pbFieldFlags = NULL;

	delete [] m_pbParamFlags;
	m_pbParamFlags = NULL;

	if (m_pvFieldProxy != NULL)
	{
		for (UINT nField = 0; nField < m_nProxyFields; nField++)
			delete m_pvFieldProxy[nField];

		delete [] m_pvFieldProxy;
		m_pvFieldProxy = NULL;
		m_nProxyFields = 0;
	}

	if (m_pvParamProxy != NULL)
	{
		for (UINT nParam = 0; nParam < m_nProxyParams; nParam++)
			delete m_pvParamProxy[nParam];

		delete [] m_pvParamProxy;
		m_pvParamProxy = NULL;
		m_nProxyParams = 0;
	}

	delete [] m_plParamLength;
	m_plParamLength = NULL;

	RETCODE nRetCode;
	if (m_hstmt != SQL_NULL_HSTMT)
	{
		AFX_SQL_SYNC(::SQLFreeStmt(m_hstmt, SQL_DROP));
		m_hstmt = SQL_NULL_HSTMT;
	}

	if (m_hstmtUpdate != SQL_NULL_HSTMT)
	{
		AFX_SQL_SYNC(::SQLFreeStmt(m_hstmtUpdate, SQL_DROP));
		m_hstmtUpdate = SQL_NULL_HSTMT;
	}

	// Remove CRecordset from CDatabase's list
	AfxLockGlobals(CRIT_ODBC);
	TRY
	{
		POSITION pos = m_pDatabase->m_listRecordsets.Find(this);
		if (pos != NULL)
			m_pDatabase->m_listRecordsets.RemoveAt(pos);
#ifdef _DEBUG
		else if (afxTraceFlags & traceDatabase)
			TRACE0("WARNING: CRecordset not found in m_pDatabase->m_listRecordsets.\n");
#endif
	}
	CATCH_ALL(e)
	{
		AfxUnlockGlobals(CRIT_ODBC);
		THROW_LAST();
	}
	END_CATCH_ALL
	AfxUnlockGlobals(CRIT_ODBC);

	m_lOpen = AFX_RECORDSET_STATUS_CLOSED;
	m_bBOF = TRUE;
	m_bEOF = TRUE;
	m_bDeleted = FALSE;
	m_bAppendable = FALSE;
	m_bUpdatable = FALSE;
	m_bScrollable = FALSE;
	m_bRebindParams = FALSE;
	m_bLongBinaryColumns = FALSE;
	m_nLockMode = optimistic;

	m_nFieldsBound = 0;
	m_nResultCols = -1;
}

BOOL CRecordset::IsOpen() const
	// Note: assumes base class CRecordset::Close called
{
	if (m_hstmt == NULL)
		return FALSE;

	if (m_lOpen == AFX_RECORDSET_STATUS_OPEN)
		return TRUE;

	RETCODE nRetCode;
	SWORD nCols;

	AFX_ODBC_CALL(::SQLNumResultCols(m_hstmt, &nCols));

	if (!Check(nRetCode))
	{
		// If function sequence error, CRecordset not open
		CDBException* e = new CDBException(nRetCode);
		e->BuildErrorString(m_pDatabase, m_hstmt, FALSE);
		if (e->m_strStateNativeOrigin.Find(_afxOutOfSequence) >= 0)
		{
			e->Delete();
			return FALSE;
		}
		else
		{
#ifdef _DEBUG
			TRACE0("Error: SQLNumResultCols failed during IsOpen().\n");
			e->TraceErrorMessage(e->m_strError);
			e->TraceErrorMessage(e->m_strStateNativeOrigin);
#endif
			THROW(e);
		}
	}

	BOOL bOpen = FALSE;

	if (nCols != 0)
		bOpen = TRUE;

	return bOpen;
}

BOOL CRecordset::IsFieldDirty(void* pv)
{
	ASSERT_VALID(this);
	ASSERT(!(m_dwOptions & useMultiRowFetch));

	if (m_nFields <= 0)
	{
		ASSERT(FALSE);
		return FALSE;
	}

	// If not in update op fields can't be dirty
	// must compare saved and current values
	if (m_nEditMode == noMode)
		return FALSE;

	// Must compare values to find dirty fields if necessary
	if (m_bCheckCacheForDirtyFields)
	{
		if (m_nEditMode == edit)
			MarkForUpdate();
		else
			MarkForAddNew();
	}

	int nIndex = 0, nIndexEnd;

	if (pv == NULL)
		nIndexEnd = m_nFields - 1;
	else
	{
		// GetBoundFieldIndex returns 1-based index
		nIndex = nIndexEnd = GetBoundFieldIndex(pv) - 1;

		// must be address of field member
		ASSERT(nIndex >= 0);
	}

	BOOL bDirty = FALSE;

	while (nIndex <= nIndexEnd && !bDirty)
		bDirty = IsFieldStatusDirty(nIndex++);

	return bDirty;
}

BOOL CRecordset::IsFieldNull(void* pv)
{
	ASSERT_VALID(this);
	ASSERT(!(m_dwOptions & useMultiRowFetch));

	int nIndex;
	BOOL bRetVal;

	if (pv == NULL)
	{
		bRetVal = FALSE;
		for (nIndex = 0; !bRetVal && nIndex <= int(m_nFields-1); nIndex++)
			bRetVal = IsFieldStatusNull((DWORD) nIndex);
	}
	else
	{
		nIndex = GetBoundFieldIndex(pv) - 1;
		if (nIndex < 0)
			ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);
		bRetVal = IsFieldStatusNull((DWORD) nIndex);
	}

	return bRetVal;
}

BOOL CRecordset::IsFieldNullable(void* pv)
{
	ASSERT_VALID(this);

	if (pv == NULL)
	{
		// Must specify valid column name
		ASSERT(FALSE);
		return FALSE;
	}

	int nIndex = GetBoundFieldIndex(pv) - 1;
	if (nIndex < 0)
		ThrowDBException(AFX_SQL_ERROR_FIELD_NOT_FOUND);

	return IsFieldNullable((DWORD)nIndex);
}

BOOL CRecordset::CanBookmark() const
{
	ASSERT_VALID(this);
	ASSERT(m_pDatabase->IsOpen());

	if (!(m_dwOptions & useBookmarks) ||
		(m_nOpenType == forwardOnly && !(m_dwOptions & useExtendedFetch)))
		return FALSE;

	return m_pDatabase->GetBookmarkPersistence() & SQL_BP_SCROLL;
}

void CRecordset::Move(long nRows, WORD wFetchType)
{
	ASSERT_VALID(this);
	ASSERT(m_hstmt != SQL_NULL_HSTMT);

	// First call - fields haven't been bound (m_nFieldsBound will change)
	if (m_nFieldsBound == 0)
	{
		InitRecord();
		ResetCursor();
	}

	if (m_nFieldsBound > 0)
	{
		// Reset field flags - mark all clean, all non-null
		memset(m_pbFieldFlags, 0, m_nFields);

		// Clear any edit mode that was set
		m_nEditMode = noMode;
	}

	// Check scrollability, EOF/BOF status
	CheckRowsetCurrencyStatus(wFetchType, nRows);

	RETCODE nRetCode;

	// Fetch the data, skipping deleted records if necessary
	if ((wFetchType == SQL_FETCH_FIRST ||
		wFetchType == SQL_FETCH_LAST ||
		wFetchType == SQL_FETCH_NEXT ||
		wFetchType == SQL_FETCH_PRIOR ||
		wFetchType == SQL_FETCH_RELATIVE) &&
		m_dwOptions & skipDeletedRecords)
	{
		SkipDeletedRecords(wFetchType, nRows, &m_dwRowsFetched, &nRetCode);
	}
	else
		// Fetch the data and check for errors
		nRetCode = FetchData(wFetchType, nRows, &m_dwRowsFetched);

	// Set currency status and increment the record counters
	SetRowsetCurrencyStatus(nRetCode, wFetchType, nRows, m_dwRowsFetched);

	// Need to fixup bound fields in some cases
	if (m_nFields > 0 && !IsEOF() && !IsBOF() &&
		!(m_dwOptions & useMultiRowFetch))
	{
		Fixups();
	}
}

void CRecordset::CheckRowsetError(RETCODE nRetCode)
{
	if (nRetCode == SQL_SUCCESS_WITH_INFO)
	{
		CDBException e(nRetCode);
		// Build the error string but don't send nuisance output to TRACE window
		e.BuildErrorString(m_pDatabase, m_hstmt, FALSE);

		if (e.m_strStateNativeOrigin.Find(_afxDataTruncated) >= 0)
		{
			// Ignore data truncated warning if binding long binary columns
			// (may mask non-long binary truncation warnings or other warnings)
			if (!((m_pDatabase->m_dwUpdateOptions & AFX_SQL_SETPOSUPDATES) &&
				m_bLongBinaryColumns))
			{
				NO_CPP_EXCEPTION(e.Empty());
				TRACE0("Error: field data truncated during data fetch.\n");
				ThrowDBException(AFX_SQL_ERROR_DATA_TRUNCATED);
			}
		}
		else if (e.m_strStateNativeOrigin.Find(_afxRowFetch) >= 0)
		{
#ifdef _DEBUG
			TRACE0("Error: fetching row from server.\n");
			e.TraceErrorMessage(e.m_strError);
			e.TraceErrorMessage(e.m_strStateNativeOrigin);
#endif
			NO_CPP_EXCEPTION(e.Empty());
			ThrowDBException(AFX_SQL_ERROR_ROW_FETCH);
		}
		else
		{
#ifdef _DEBUG
			// Not a truncation or row fetch warning so send debug output
			if (afxTraceFlags & traceDatabase)
			{
				TRACE0("Warning: ODBC Success With Info,\n");
				e.TraceErrorMessage(e.m_strError);
				e.TraceErrorMessage(e.m_strStateNativeOrigin);
			}
#endif // _DEBUG
		}
	}
	else if (!Check(nRetCode))
		ThrowDBException(nRetCode);
}

void CRecordset::GetBookmark(CDBVariant& varBookmark)
{
	ASSERT_VALID(this);

	// Validate bookmarks are usable
	if (!(m_dwOptions & useBookmarks))
		ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_ENABLED);
	else if (!CanBookmark())
		ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_SUPPORTED);

	// Currently ODBC only supports 4 byte bookmarks
	// Initialize the variant to a long
	if (varBookmark.m_dwType != DBVT_LONG)
	{
		varBookmark.Clear();
		varBookmark.m_dwType = DBVT_LONG;
		varBookmark.m_lVal = 0;
	}

	RETCODE nRetCode;
	SDWORD nActualSize;

	// Retrieve the bookmark (column 0) data
	AFX_ODBC_CALL(::SQLGetData(m_hstmt, 0, SQL_C_BOOKMARK,
		&varBookmark.m_lVal, sizeof(varBookmark.m_lVal), &nActualSize));
	if (!Check(nRetCode))
	{
		TRACE0("Error: GetBookmark operation failed.\n");
		ThrowDBException(nRetCode);
	}
}

void CRecordset::SetBookmark(const CDBVariant& varBookmark)
{
	ASSERT_VALID(this);

	// Validate bookmarks are usable
	if (!(m_dwOptions & useBookmarks))
		ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_ENABLED);
	else if (!CanBookmark())
		ThrowDBException(AFX_SQL_ERROR_BOOKMARKS_NOT_SUPPORTED);

	// Currently ODBC only supports 4 byte bookmarks
	ASSERT(varBookmark.m_dwType == DBVT_LONG);

	Move(varBookmark.m_lVal, SQL_FETCH_BOOKMARK);
}

void CRecordset::SetRowsetSize(DWORD dwNewRowsetSize)
{
	ASSERT_VALID(this);
	ASSERT(dwNewRowsetSize > 0);

	// If not yet open, only set expected length
	if (!IsOpen())
	{
		m_dwRowsetSize = dwNewRowsetSize;
		return;
	}

	if (!(m_dwOptions & useMultiRowFetch))
	{
		// Only works if bulk row fetching!
		ASSERT(FALSE);
		return;
	}

	// Need to reallocate some memory if rowset size grows
	if (m_dwAllocatedRowsetSize == 0 ||
		(m_dwAllocatedRowsetSize < dwNewRowsetSize))
	{
		// If rowset already allocated, delete old and reallocate
		FreeRowset();
		m_rgRowStatus = new WORD[dwNewRowsetSize];

		// If not a user allocated buffer grow the data buffers
		if (!(m_dwOptions & userAllocMultiRowBuffers))
		{
			// Allocate the rowset field buffers
			m_dwRowsetSize = dwNewRowsetSize;
			CFieldExchange fx(CFieldExchange::AllocMultiRowBuffer, this);
			DoBulkFieldExchange(&fx);

			m_dwAllocatedRowsetSize = dwNewRowsetSize;

			// Set bound fields to zero, rebind and reset bound field count
			int nOldFieldsBound = m_nFieldsBound;
			m_nFieldsBound = 0;
			InitRecord();
			m_nFieldsBound = nOldFieldsBound;
		}
	}
	else
	{
		// Just reset the new rowset size
		m_dwRowsetSize = dwNewRowsetSize;
	}

	RETCODE nRetCode;
	AFX_SQL_SYNC(::SQLSetStmtOption(m_hstmt, SQL_ROWSET_SIZE,
		m_dwRowsetSize));
}

void CRecordset::AddNew()
{
	ASSERT_VALID(this);
	ASSERT(m_hstmt != SQL_NULL_HSTMT);
	// we can't construct an INSERT statement w/o any columns
	ASSERT(m_nFields != 0);

	if (!m_bAppendable)
	{
		ThrowDBException(AFX_SQL_ERROR_RECORDSET_READONLY);
	}

	if (m_dwOptions & useMultiRowFetch)
	{
		// Can't use update methods on multi-row rowset
		ASSERT(FALSE);
		return;
	}

	if (m_bCheckCacheForDirtyFields && m_nFields > 0)
	{
		if (m_nEditMode == noMode)
		{

⌨️ 快捷键说明

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