Click here to Skip to main content
15,886,199 members
Articles / Database Development / SQL Server

Asynchronous Query Execution

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
8 Dec 1999 82.1K   806   32  
Two classes that provide support for asynchronous SQL execution.
// AsyncRecordset.cpp : implementation file
//
#include "stdafx.h"
#include "afxconv.h"
#include "AsyncRecordset.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAsyncRecordset
static const TCHAR szDriverNotCapable[] = _T("State:S1C00");

IMPLEMENT_DYNAMIC(CAsyncRecordset, CDynamicRecordset)

CAsyncRecordset::CAsyncRecordset(CDatabase* pdb) : CDynamicRecordset(pdb)
{
	//{{AFX_FIELD_INIT(CAsyncRecordset)
	//}}AFX_FIELD_INIT
	m_nDefaultType = snapshot;
	m_bUnbound = FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CAsyncRecordset diagnostics
#ifdef _DEBUG
void CAsyncRecordset::AssertValid() const
{
	CDynamicRecordset::AssertValid();
}

void CAsyncRecordset::Dump(CDumpContext& dc) const
{
	CDynamicRecordset::Dump(dc);
}
#endif //_DEBUG

void CAsyncRecordset::OnSetOptions(HSTMT hstmt)
{
	CDynamicRecordset::OnSetOptions(hstmt);

	RETCODE nRetCode = ::SQLSetStmtOption(hstmt, SQL_ASYNC_ENABLE, SQL_ASYNC_ENABLE_ON);
	if(!Check(nRetCode))
	{
		TRACE("Warninig! Asynchronous execution not supported?");
		//ThrowDBException(nRetCode);
	}
}

// OpenAsync
BOOL CAsyncRecordset::OpenAsync(UINT nOpenType, LPCTSTR lpszSQL, DWORD dwOptions)
{
	ASSERT(!IsOpen());
	ASSERT_VALID(this);
	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
		m_bUnbound = FALSE;
		if(m_nFields > 0 || m_nParams > 0)
			AllocStatusArrays();
		else
			m_bUnbound = TRUE;

		// Build SQL and execute direct
		BuildSQL(lpszSQL);
		PrepareAndExecute();
	}
	CATCH_ALL(e)
	{
		Close();
		THROW_LAST();
	}
	END_CATCH_ALL

	return TRUE;
}

BOOL CAsyncRecordset::StillExecuting()
{
	USES_CONVERSION;

	ASSERT(!IsOpen());
	ASSERT_VALID(this);
	RETCODE nRetCode;
	LPCSTR lpszWSQL = T2CA(m_strSQL);

	TRY
	{
		nRetCode = ::SQLExecDirect(m_hstmt, (UCHAR*)lpszWSQL, SQL_NTS);

		// check retcode
		switch (nRetCode)
		{
			case 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
				}

				break;
			}
			case SQL_SUCCESS:
			case SQL_NO_DATA_FOUND:
				break;
			case SQL_STILL_EXECUTING:
				return TRUE;
				break;
			default:
				ThrowDBException(nRetCode);
		}

		m_lOpen = AFX_RECORDSET_STATUS_OPEN;

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

		// If late binding, still need to allocate status arrays
		if(m_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 FALSE;
}

void CAsyncRecordset::PrepareAndExecute()
{
	USES_CONVERSION;
	RETCODE nRetCode;
	LPCSTR lpszWSQL = T2CA(m_strSQL);
	BOOL bConcurency = FALSE;

	while(!bConcurency)
	{
		nRetCode = ::SQLExecDirect(m_hstmt, (UCHAR*)lpszWSQL, SQL_NTS);

		// check retcode
		switch (nRetCode)
		{
			case SQL_SUCCESS_WITH_INFO:
			case SQL_SUCCESS:
			case SQL_NO_DATA_FOUND:
			case SQL_STILL_EXECUTING:
				bConcurency = TRUE;
				break;
			default:
			{
				// 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(szDriverNotCapable) >= 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);
				}
			}
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions