// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 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"
#include "math.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

//////////////////////////////////////////////////////////////////////////
// Helpers

// Helpers for floating point operations
static const float afxFloatPseudoNull = AFX_RFX_SINGLE_PSEUDO_NULL;
static const double afxDoublePseudoNull = AFX_RFX_DOUBLE_PSEUDO_NULL;

// Long binary allocation helper
void AFX_CDECL AllocLongBinary(CLongBinary& lb, DWORD dwDataLength);

// Memory allocation callbacks used by ICDAOGetRows
STDAPI DaoStringAllocCallback(DWORD dwLen, DWORD pData, void** ppv);
STDAPI DaoBinaryAllocCallback(DWORD dwLen, DWORD pData, void** ppv);
STDAPI DaoLongBinaryAllocCallback(DWORD dwLen, DWORD pData, void** ppv);

//////////////////////////////////////////////////////////////////////////
// CDaoFieldExchange

CDaoFieldExchange::CDaoFieldExchange(UINT nOperation,
	CDaoRecordset* prs, void* pvField)
{
	ASSERT(nOperation < MaxDFXOperation);

	m_nFieldType = none;
	m_nOperation = nOperation;
	m_prs = prs;
	m_pvField = pvField;
	m_nField = 0;
	m_nParam = 0;
}

BOOL CDaoFieldExchange::IsValidOperation()
{
	if (m_nOperation >= MaxDFXOperation)
	{
		// Invalid operation
		ASSERT(FALSE);
		return FALSE;
	}

	// Operations valid for both field types
#ifdef _DEBUG
	if (m_nOperation == DumpField || m_nOperation == SetFieldNull)
		return TRUE;
#endif

	// Operations only valid for outputColumn OR param types
	if ((m_nOperation == AddToParameterList) ||
		(m_nOperation == BindParam))
		return (m_nFieldType == param);
	else
		return (m_nFieldType == outputColumn);
}

void CDaoFieldExchange::AppendParamType(CString& strParamList,
	DWORD dwParamType)
{
	switch (dwParamType)
	{
	default:
		ASSERT(FALSE);
		break;

	case AFX_RFX_TEXT:
		strParamList += " Text";
		break;

	case AFX_RFX_BINARY:
		strParamList += " LongBinary";
		break;

	case AFX_RFX_LONGBINARY:
		strParamList += " LongBinary";
		break;

	case AFX_RFX_BOOL:
		strParamList += " Bit";
		break;

	case AFX_RFX_BYTE:
		strParamList += " Byte";
		break;

	case AFX_RFX_SHORT:
		strParamList += " Short";
		break;

	case AFX_RFX_LONG:
		strParamList += " Long";
		break;

	case AFX_RFX_CURRENCY:
		strParamList += " Currency";
		break;

	case AFX_RFX_SINGLE:
		strParamList += " IEEESingle";
		break;

	case AFX_RFX_DOUBLE:
		strParamList += " IEEEDouble";
		break;

	case AFX_RFX_DATE:
		strParamList += " DateTime";
		break;
	}
}

CDaoFieldCache* CDaoFieldExchange::GetCacheValue(CDaoRecordset* prs, void* pv)
{
		void* pvCache;

		// Lookup storage locations
		if (!prs->m_pMapFieldCache->Lookup(pv, pvCache))
			AfxThrowDaoException(AFX_DAO_ERROR_DFX_BIND);
		return (CDaoFieldCache*)pvCache;
}

void CDaoFieldExchange::SetNullValue(void* pv, DWORD dwDataType)
{
	switch (dwDataType)
	{
	default:
		ASSERT(FALSE);
		break;

	case AFX_RFX_TEXT:
		((CString*)pv)->Empty();
		break;

	case AFX_RFX_BINARY:
		((CByteArray*)pv)->SetSize(0);
		break;

	case AFX_RFX_LONGBINARY:
		((CLongBinary*)pv)->m_dwDataLength = 0;
		break;

	case AFX_RFX_BOOL:
		*(BOOL*)pv = AFX_RFX_BOOL_PSEUDO_NULL;
		break;

	case AFX_RFX_BYTE:
		*(BYTE*)pv = AFX_RFX_BYTE_PSEUDO_NULL;
		break;

	case AFX_RFX_SHORT:
		*(short*)pv = AFX_RFX_SHORT_PSEUDO_NULL;
		break;

	case AFX_RFX_LONG:
		*(long*)pv = AFX_RFX_LONG_PSEUDO_NULL;
		break;

	case AFX_RFX_CURRENCY:
		((COleCurrency*)pv)->SetStatus(COleCurrency::null);
		break;

	case AFX_RFX_SINGLE:
		*(float*)pv = afxFloatPseudoNull;
		break;

	case AFX_RFX_DOUBLE:
		*(double*)pv = afxDoublePseudoNull;
		break;

	case AFX_RFX_DATE:
		((COleDateTime*)pv)->SetStatus(COleDateTime::null);
		break;
	}
}

BOOL CDaoFieldExchange::IsNullValue(void* pv, DWORD dwDataType)
{
	BOOL bNull = FALSE;

	switch (dwDataType)
	{
	default:
		ASSERT(FALSE);
		break;

	case AFX_RFX_TEXT:
		if (((CString*)pv)->IsEmpty())
			bNull = TRUE;
		break;

	case AFX_RFX_BINARY:
		if (((CByteArray*)pv)->GetSize() == 0)
			bNull = TRUE;
		break;

	case AFX_RFX_LONGBINARY:
		if (((CLongBinary*)pv)->m_dwDataLength == 0)
			bNull = TRUE;
		break;

	case AFX_RFX_BOOL:
		if (*(BOOL*)pv == AFX_RFX_BOOL_PSEUDO_NULL)
			bNull = TRUE;
		break;

	case AFX_RFX_BYTE:
		if (*(BYTE*)pv == AFX_RFX_BYTE_PSEUDO_NULL)
			bNull = TRUE;
		break;

	case AFX_RFX_SHORT:
		if (*(short*)pv == AFX_RFX_SHORT_PSEUDO_NULL)
			bNull = TRUE;
		break;

	case AFX_RFX_LONG:
		if (*(long*)pv == AFX_RFX_LONG_PSEUDO_NULL)
			bNull = TRUE;
		break;

	case AFX_RFX_CURRENCY:
		if (((COleCurrency*)pv)->GetStatus() == COleCurrency::null)
			bNull = TRUE;
		break;

	case AFX_RFX_SINGLE:
		if (*(float*)pv == afxFloatPseudoNull)
			bNull = TRUE;
		break;

	case AFX_RFX_DOUBLE:
		if (*(double*)pv == afxDoublePseudoNull)
			bNull = TRUE;
		break;

	case AFX_RFX_DATE:
		if (((COleDateTime*)pv)->GetStatus() == COleDateTime::null)
			bNull = TRUE;
		break;
	}

	return bNull;
}

void CDaoFieldExchange::AllocCacheValue(CDaoFieldCache*& pCache,
	DWORD dwDataType)
{
	// Initialize a new field cache
	pCache = new CDaoFieldCache;
	pCache->m_nStatus = 0;

	switch (dwDataType)
	{
	default:
		ASSERT(FALSE);
		break;

	case AFX_RFX_TEXT:
		pCache->m_pvData = new CString();
		pCache->m_nDataType = AFX_RFX_TEXT;
		break;

	case AFX_RFX_BINARY:
		pCache->m_pvData = new CByteArray();
		pCache->m_nDataType = AFX_RFX_BINARY;
		break;

	case AFX_RFX_LONGBINARY:
		pCache->m_pvData = new CLongBinary();
		pCache->m_nDataType = AFX_RFX_LONGBINARY;
		break;

	case AFX_RFX_BOOL:
		pCache->m_pvData = new BOOL;
		pCache->m_nDataType = AFX_RFX_BOOL;
		break;

	case AFX_RFX_BYTE:
		pCache->m_pvData = new BYTE;
		pCache->m_nDataType = AFX_RFX_BYTE;
		break;

	case AFX_RFX_SHORT:
		pCache->m_pvData = new short;
		pCache->m_nDataType = AFX_RFX_SHORT;
		break;

	case AFX_RFX_LONG:
		pCache->m_pvData = new long;
		pCache->m_nDataType = AFX_RFX_LONG;
		break;

	case AFX_RFX_CURRENCY:
		pCache->m_pvData = new COleCurrency();
		pCache->m_nDataType = AFX_RFX_CURRENCY;
		break;

	case AFX_RFX_SINGLE:
		pCache->m_pvData = new float;
		pCache->m_nDataType = AFX_RFX_SINGLE;
		break;

	case AFX_RFX_DOUBLE:
		pCache->m_pvData = new double;
		pCache->m_nDataType = AFX_RFX_DOUBLE;
		break;

	case AFX_RFX_DATE:
		pCache->m_pvData = new COleDateTime();
		pCache->m_nDataType = AFX_RFX_DATE;
		break;
	}
}

void CDaoFieldExchange::DeleteCacheValue(CDaoFieldCache* pCache,
	DWORD dwDataType)
{
	switch (dwDataType)
	{
	default:
		ASSERT(FALSE);
		break;

	case AFX_RFX_TEXT:
		delete (CString*)pCache->m_pvData;
		break;

	case AFX_RFX_BINARY:
		delete (CByteArray*)pCache->m_pvData;
		break;

	case AFX_RFX_LONGBINARY:
		delete (CLongBinary*)pCache->m_pvData;
		break;

	case AFX_RFX_BOOL:
		delete (BOOL*)pCache->m_pvData;
		break;

	case AFX_RFX_BYTE:
		delete (BYTE*)pCache->m_pvData;
		break;

	case AFX_RFX_SHORT:
		delete (short*)pCache->m_pvData;
		break;

	case AFX_RFX_LONG:
		delete (long*)pCache->m_pvData;
		break;

	case AFX_RFX_CURRENCY:
		delete (COleCurrency*)pCache->m_pvData;
		break;

	case AFX_RFX_SINGLE:
		delete (float*)pCache->m_pvData;
		break;

	case AFX_RFX_DOUBLE:
		delete (double*)pCache->m_pvData;
		break;

	case AFX_RFX_DATE:
		delete (COleDateTime*)pCache->m_pvData;
		break;
	}

	delete pCache;
	pCache = NULL;
}

void CDaoFieldExchange::CopyValue(void* pvSrc, void* pvDest,
	DWORD dwDataType)
{
	switch (dwDataType)
	{
	default:
		ASSERT(FALSE);
		break;

	case AFX_RFX_TEXT:
		*(CString*)pvDest = *(CString*)pvSrc;
		break;

	case AFX_RFX_BINARY:
		((CByteArray*)pvDest)->Copy(*(CByteArray*)pvSrc);
		break;

	case AFX_RFX_LONGBINARY:
		{
			CLongBinary* pLongBinarySrc = (CLongBinary*)pvSrc;
			CLongBinary* pLongBinaryDest = (CLongBinary*)pvDest;

			// Reallocate memory in destination if necessary
			AllocLongBinary(*pLongBinaryDest, pLongBinarySrc->m_dwDataLength);
			pLongBinaryDest->m_dwDataLength =
				pLongBinarySrc->m_dwDataLength;

			BYTE* pbSrc = (BYTE*)::GlobalLock(pLongBinarySrc->m_hData);
			BYTE* pbDest = (BYTE*)::GlobalLock(pLongBinaryDest->m_hData);
			memcpy(pbDest, pbSrc, pLongBinarySrc->m_dwDataLength);
			::GlobalUnlock(pLongBinarySrc->m_hData);
			::GlobalUnlock(pLongBinaryDest->m_hData);
		}
		break;

	case AFX_RFX_BOOL:
		*(BOOL*)pvDest = *(BOOL*)pvSrc;
		break;

	case AFX_RFX_BYTE:
		*(BYTE*)pvDest = *(BYTE*)pvSrc;
		break;

	case AFX_RFX_SHORT:
		*(short*)pvDest = *(short*)pvSrc;
		break;

	case AFX_RFX_LONG:
		*(long*)pvDest = *(long*)pvSrc;
		break;

	case AFX_RFX_CURRENCY:
		*(COleCurrency*)pvDest = *(COleCurrency*)pvSrc;
		break;

	case AFX_RFX_SINGLE:
		*(float*)pvDest = *(float*)pvSrc;
		break;

	case AFX_RFX_DOUBLE:
		*(double*)pvDest = *(double*)pvSrc;
		break;

	case AFX_RFX_DATE:
		*(COleDateTime*)pvDest = *(COleDateTime*)pvSrc;
		break;
	}
}

BOOL CDaoFieldExchange::CompareValue(void* pvSrc, void* pvDest,
	DWORD dwDataType)
{
	BOOL bDirty = FALSE;

	switch (dwDataType)
	{
	default:
		ASSERT(FALSE);
		break;

	case AFX_RFX_TEXT:
		if (*(CString*)pvDest != *(CString*)pvSrc)
			bDirty = TRUE;
		break;

	case AFX_RFX_BINARY:
		{
			CByteArray* pByteArraySrc = (CByteArray*)pvSrc;
			CByteArray* pByteArrayDest = (CByteArray*)pvDest;
			int nSize = pByteArraySrc->GetSize();

			if (memcmp(&pByteArrayDest[0], &pByteArraySrc[0], nSize) != 0)
				bDirty = TRUE;
		}
		break;

	case AFX_RFX_LONGBINARY:
		{
			CLongBinary* pLongBinarySrc = (CLongBinary*)pvSrc;
			CLongBinary* pLongBinaryDest = (CLongBinary*)pvDest;

			BYTE* pbSrc = (BYTE*)::GlobalLock(pLongBinarySrc->m_hData);
			BYTE* pbDest = (BYTE*)::GlobalLock(pLongBinaryDest->m_hData);

			if (memcmp(pbDest, pbSrc, pLongBinarySrc->m_dwDataLength) != 0)
				bDirty = TRUE;

			::GlobalUnlock(pLongBinarySrc->m_hData);
			::GlobalUnlock(pLongBinaryDest->m_hData);
		}
		break;

	case AFX_RFX_BOOL:
		if (*(BOOL*)pvDest != *(BOOL*)pvSrc)
			bDirty = TRUE;
		break;

	case AFX_RFX_BYTE:
		if (*(BYTE*)pvDest != *(BYTE*)pvSrc)
			bDirty = TRUE;
		break;

	case AFX_RFX_SHORT:
		if (*(short*)pvDest != *(short*)pvSrc)
			bDirty = TRUE;
		break;

	case AFX_RFX_LONG:
		if (*(long*)pvDest != *(long*)pvSrc)
			bDirty = TRUE;
		break;

	case AFX_RFX_CURRENCY:
		if (*(COleCurrency*)pvDest != *(COleCurrency*)pvSrc)
			bDirty = TRUE;
		break;

	case AFX_RFX_SINGLE:
		if (*(float*)pvDest != *(float*)pvSrc)
			bDirty = TRUE;
		break;

	case AFX_RFX_DOUBLE:
		if (*(double*)pvDest != *(double*)pvSrc)
			bDirty = TRUE;
		break;

	case AFX_RFX_DATE:
		if (*(COleDateTime*)pvDest != *(COleDateTime*)pvSrc)
			bDirty = TRUE;
		break;
	}

	return bDirty;
}

void CDaoFieldExchange::FillVariant(void* pvValue, DWORD dwDataType,
	COleVariant** ppVar)
{
	COleVariant* pVar;

	if (dwDataType == AFX_RFX_TEXT)
		pVar = new COleVariant(*(CString*)pvValue, VT_BSTRT);
	else if (dwDataType == AFX_RFX_BOOL)
		pVar = new COleVariant(*(long*)pvValue, VT_BOOL);
	else
	{
		pVar = new COleVariant;

		switch (dwDataType)
		{
		default:
			ASSERT(FALSE);
			break;

		case AFX_RFX_BINARY:
			*pVar = *(CByteArray*)pvValue;
			break;

		case AFX_RFX_LONGBINARY:
			*pVar = *(CLongBinary*)pvValue;
			break;

		case AFX_RFX_BYTE:
			*pVar = *(BYTE*)pvValue;
			break;

		case AFX_RFX_SHORT:
			*pVar = *(short*)pvValue;
			break;

		case AFX_RFX_LONG:
			*pVar = *(long*)pvValue;
			break;

		case AFX_RFX_CURRENCY:
			*pVar = *(COleCurrency*)pvValue;
			break;

		case AFX_RFX_SINGLE:
			*pVar = *(float*)pvValue;
			break;

		case AFX_RFX_DOUBLE:
			*pVar = *(double*)pvValue;
			break;

		case AFX_RFX_DATE:
			*pVar = *(COleDateTime*)pvValue;
		}
	}

	*ppVar = pVar;
}

// Default implementation for RFX functions
void CDaoFieldExchange::Default(LPCTSTR lpszName, void* pv,
	DWORD dwColumnType, DWORD dwBindOptions)
{
	switch (m_nOperation)
	{
	case AddToParameterList:
		if (m_nParam != 1)
			m_prs->m_strSQL += ",";
		m_prs->m_strSQL += lpszName;
		AppendParamType(m_prs->m_strSQL, dwColumnType);
		return;

	case AddToSelectList:
		if (m_nField != 1)
			m_prs->m_strSQL += ",";
		m_prs->m_strSQL += lpszName;
		return;

	case BindField:
		{
			// Query parser needs "[" & "]", GetRows can't tolerate them.
			LPTSTR lpszNoBracketName = new TCHAR[_tcslen(lpszName) + 1];
			m_prs->StripBrackets(lpszName, lpszNoBracketName);

			// Finish setting up column binding info struct
			LPDAOCOLUMNBINDING pcb =
				&m_prs->m_prgDaoColBindInfo[m_nField-1];
			pcb->cbInfoOffset =
				(DWORD)&m_prs->m_pulColumnLengths[m_nField-1];
#ifndef _UNICODE
			pcb->columnID.dwKind = DAOCOLKIND_STR;
			pcb->columnID.lpstr = lpszNoBracketName;
#else
			pcb->columnID.dwKind = DAOCOLKIND_WSTR;
			pcb->columnID.lpwstr = lpszNoBracketName;
#endif

			// Setup the field index map (and store value as void ptr)
			m_prs->m_pMapFieldIndex->SetAt(pv, (void*)m_nField);
		}
		return;

	case BindParam:
		{
			COleVariant* pvar = NULL;

			TRY
			{
				// NULL params not supported, use IS NULL in SQL
				// (i.e. - m_strFilter = _T("Foo IS NULL");
				FillVariant(pv, dwColumnType, &pvar);
				m_prs->m_pQueryDef->SetParamValue(lpszName, *pvar);
			}
			CATCH_ALL(e)
			{
				if (pvar != NULL)
					pvar->Clear();
				delete pvar;
				pvar = NULL;
				THROW_LAST();
			}
			END_CATCH_ALL

			pvar->Clear();
			delete pvar;
			pvar = NULL;
		}
		return;

	case Fixup:
		if (m_prs->GetFieldLength(m_nField-1) == DAO_NULL)
		{
			// Set the value to PSEUDO NULL and mark the status NULL
			SetNullValue(pv, dwColumnType);
			m_prs->SetNullFieldStatus(m_nField-1);
		}
		return;

	case AllocCache:
		if (dwBindOptions & AFX_DAO_ENABLE_FIELD_CACHE)
		{
			CDaoFieldCache* pCache;

			// Allocate new storage and add to map
			AllocCacheValue(pCache, dwColumnType);
			m_prs->m_pMapFieldCache->SetAt(pv, pCache);
		}
		return;

	case StoreField:
		if (dwBindOptions & AFX_DAO_ENABLE_FIELD_CACHE)
		{
			CDaoFieldCache* pCache = GetCacheValue(m_prs, pv);

			// Copy the data to the cache
			CopyValue(pv, pCache->m_pvData, dwColumnType);

			// Cache the NULL status
			if (m_prs->IsFieldStatusNull(m_nField-1))
				pCache->m_nStatus |= AFX_DAO_FIELD_FLAG_NULL;
			else
				pCache->m_nStatus &= ~AFX_DAO_FIELD_FLAG_NULL;
		}
		return;

	case LoadField:
		if (dwBindOptions & AFX_DAO_ENABLE_FIELD_CACHE)
		{
			CDaoFieldCache* pCache = GetCacheValue(m_prs, pv);

			// Copy the data from the cache
			CopyValue(pCache->m_pvData, pv, dwColumnType);

			// Set the NULL status from the cache
			if (pCache->m_nStatus & AFX_DAO_FIELD_FLAG_NULL)
				m_prs->SetNullFieldStatus(m_nField-1);
			else
				m_prs->ClearNullFieldStatus(m_nField-1);
		}
		return;

	case SetFieldNull:
		// Setting field NOT NULL doesn't require field exchange
		if ((m_pvField == NULL && m_nFieldType == outputColumn)
			|| m_pvField == pv)
		{
			SetNullValue(pv, dwColumnType);

			// Also set the status array if not a parameter
			if (m_nFieldType == outputColumn)
				m_prs->SetNullFieldStatus(m_nField-1);

#ifdef _DEBUG
			m_nFieldFound = m_nField;
#endif
		}
		return;

	case MarkForAddNew:
		if (dwBindOptions & AFX_DAO_ENABLE_FIELD_CACHE)
		{
			// Don't need to do anything if field marked dirty
			if (!m_prs->IsFieldStatusDirty(m_nField-1))
			{
				// Mark dirty & not NULL if not set to pseudo NULL value
				if (!IsNullValue(pv, dwColumnType))
				{
					m_prs->SetDirtyFieldStatus(m_nField-1);
					m_prs->ClearNullFieldStatus(m_nField-1);
				}
			}
		}
		return;

	case MarkForEdit:
		if (dwBindOptions & AFX_DAO_ENABLE_FIELD_CACHE)
		{
			// If value not pseudo NULL value, clear NULL status
			if (!IsNullValue(pv, dwColumnType))
				m_prs->ClearNullFieldStatus(m_nField-1);

			// If field already marked dirty, don't need to check cache
			if (!m_prs->IsFieldStatusDirty(m_nField-1))
			{
				CDaoFieldCache* pCache = GetCacheValue(m_prs, pv);

				BOOL bNullField = m_prs->IsFieldStatusNull(m_nField-1);
				BOOL bNullCache = pCache->m_nStatus & AFX_DAO_FIELD_FLAG_NULL;

				// Mark dirty if NULL status differs or value differs
				if ( (bNullCache && !bNullField) ||
					(!bNullCache && bNullField) ||
					CompareValue(pv, pCache->m_pvData, dwColumnType))
				{
					m_prs->SetDirtyFieldStatus(m_nField-1);
				}
			}
		}
		return;

	case SetDirtyField:
		if (m_prs->IsFieldStatusDirty(m_nField-1))
		{
			COleVariant* pvar = NULL;

			TRY
			{
				// If field is NULL don't set the value
				if (!m_prs->IsFieldStatusNull(m_nField-1))
					FillVariant(pv, dwColumnType, &pvar);
				else
				{
					pvar = new COleVariant;
					pvar->vt = VT_NULL;
				}

				// SetFieldValue (put_Collect) doesn't like brackets
				// Assumes no brackets if first char not a bracket
				LPTSTR lpszModifiedName = NULL;
				if (*lpszName == '[')
				{
					lpszModifiedName = new TCHAR[_tcslen(lpszName) + 1];

					// Copy the name with no brackets, and reset lpszName
					m_prs->StripBrackets(lpszName, lpszModifiedName);
					lpszName = lpszModifiedName;
				}

				m_prs->SetFieldValue(lpszName, *pvar);
				delete lpszModifiedName;
			}
			CATCH_ALL(e)
			{
				if (pvar != NULL)
					pvar->Clear();
				delete pvar;
				pvar = NULL;
				THROW_LAST();
			}
			END_CATCH_ALL

			pvar->Clear();
			delete pvar;
			pvar = NULL;
		}
		return;

	default:
		ASSERT(FALSE);
		return;
	}
}

void AFXAPI DFX_Text(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	CString& value, int nPreAllocSize, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	DWORD dwDAOType;

#ifdef _UNICODE
	dwDAOType = DAO_WCHAR;
#else
	dwDAOType = DAO_CHAR;
#endif

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			// Pre-allocate buffer to prevent needless re-allocations
			value.GetBuffer(nPreAllocSize);
			value.ReleaseBuffer();

			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = dwDAOType;
			pcb->dwBinding = DAOBINDING_DIRECT | DAOBINDING_CALLBACK;
			pcb->dwUser = (DWORD)&value;
			pcb->cbDataOffset = (DWORD)DaoStringAllocCallback;
			pcb->cbMaxLen = INT_MAX;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_TEXT,
			dwBindOptions);
		return;

	case CDaoFieldExchange::Fixup:
		if (pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] == 0 ||
			pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] == DAO_NULL)
		{
			// If null or empty string set length zero
			value.GetBufferSetLength(0);
		}
		else
		{
			// Make sure length correct (and doesn't include NULL terminator)
			value.GetBufferSetLength(
				pFX->m_prs->m_pulColumnLengths[pFX->m_nField-1] - 1);
		}

		pFX->Default(lpszName, (void*)&value,
			AFX_RFX_TEXT, dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_Binary(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	CByteArray& value, int nPreAllocSize, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			// Pre-allocate buffer to prevent needless re-allocations
			value.SetSize(nPreAllocSize);

			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_BYTES;
			pcb->dwBinding = DAOBINDING_DIRECT | DAOBINDING_CALLBACK;
			pcb->dwUser = (DWORD)&value;
			pcb->cbDataOffset = (ULONG)DaoBinaryAllocCallback;
			pcb->cbMaxLen = INT_MAX;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_BINARY,
			dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_LongBinary(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	CLongBinary& value, DWORD dwPreAllocSize, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			// Pre-allocate buffer to prevent needless re-allocations
			AllocLongBinary(value, dwPreAllocSize);

			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_BYTES;
			pcb->dwBinding = DAOBINDING_DIRECT | DAOBINDING_CALLBACK;
			pcb->dwUser = (DWORD)&value;
			pcb->cbDataOffset = (DWORD)DaoLongBinaryAllocCallback;
			pcb->cbMaxLen = ULONG_MAX;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value,
			AFX_RFX_LONGBINARY, dwBindOptions);
		return;

	case CDaoFieldExchange::Fixup:
		// Unlock data locked in DaoLongBinaryAllocCallback
		if (value.m_dwDataLength != 0)
			::GlobalUnlock(value.m_hData);

		pFX->Default(lpszName, (void*)&value,
			AFX_RFX_LONGBINARY, dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_Bool(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	BOOL& value, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_BOOL;
			pcb->dwBinding = DAOBINDING_DIRECT;
			pcb->cbDataOffset = (DWORD)&value;
			pcb->dwUser = 0;
			pcb->cbMaxLen = sizeof(value);

			pFX->m_prs->m_cbFixedLengthFields += pcb->cbMaxLen;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_BOOL,
			dwBindOptions);
		return;

	case CDaoFieldExchange::Fixup:
		// Convert BOOL value from AFX_DAO_TRUE/FALSE to TRUE/FALSE
		value = (value != AFX_DAO_FALSE);

		pFX->Default(lpszName, (void*)&value,
			AFX_RFX_LONGBINARY, dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_Byte(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	BYTE& value, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_BYTE;
			pcb->dwBinding = DAOBINDING_DIRECT;
			pcb->cbDataOffset = (DWORD)&value;
			pcb->dwUser = 0;
			pcb->cbMaxLen = sizeof(value);

			pFX->m_prs->m_cbFixedLengthFields += pcb->cbMaxLen;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_BYTE,
			dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_Short(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	short& value, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_I2;
			pcb->dwBinding = DAOBINDING_DIRECT;
			pcb->cbDataOffset = (DWORD)&value;
			pcb->dwUser = 0;
			pcb->cbMaxLen = sizeof(value);

			pFX->m_prs->m_cbFixedLengthFields += pcb->cbMaxLen;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_SHORT,
			dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_Long(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	long& value, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_I4;
			pcb->dwBinding = DAOBINDING_DIRECT;
			pcb->cbDataOffset = (DWORD)&value;
			pcb->dwUser = 0;
			pcb->cbMaxLen = sizeof(value);

			pFX->m_prs->m_cbFixedLengthFields += pcb->cbMaxLen;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_LONG,
			dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_Currency(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	COleCurrency& value, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_CURRENCY;
			pcb->dwBinding = DAOBINDING_DIRECT;
			pcb->cbDataOffset = (DWORD)&value.m_cur;
			pcb->dwUser = 0;
			pcb->cbMaxLen = sizeof(CURRENCY);

			pFX->m_prs->m_cbFixedLengthFields += pcb->cbMaxLen;

			// Finish setting up column binding struct
			pFX->Default(lpszName, (void*)&value, AFX_RFX_CURRENCY,
				dwBindOptions);
			return;
		}

	case CDaoFieldExchange::Fixup:
		// Must reset the valid currency flag
		if (pFX->m_prs->GetFieldLength(pFX->m_nField-1) == DAO_NULL)
			value.SetStatus(COleCurrency::null);
		else
			value.SetStatus(COleCurrency::valid);

		// Fall through to reset the NULL status

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_CURRENCY,
			dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << ":" << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_Single(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	float& value, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_R4;
			pcb->dwBinding = DAOBINDING_DIRECT;
			pcb->cbDataOffset = (DWORD)&value;
			pcb->dwUser = 0;
			pcb->cbMaxLen = sizeof(value);

			pFX->m_prs->m_cbFixedLengthFields += pcb->cbMaxLen;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_SINGLE,
			dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_Double(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	double& value, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_R8;
			pcb->dwBinding = DAOBINDING_DIRECT;
			pcb->cbDataOffset = (DWORD)&value;
			pcb->dwUser = 0;
			pcb->cbMaxLen = sizeof(value);

			pFX->m_prs->m_cbFixedLengthFields += pcb->cbMaxLen;
		}
		// Fall through to finish setting up column binding struct

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_DOUBLE,
			dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << " = " << value;
		return;
#endif //_DEBUG
	}
}

void AFXAPI DFX_DateTime(CDaoFieldExchange* pFX, LPCTSTR lpszName,
	COleDateTime& value, DWORD dwBindOptions)
{
	(pFX->m_nFieldType == CDaoFieldExchange::outputColumn) ?
		++pFX->m_nField: ++pFX->m_nParam;

	// Do nothing if op not supported for outputColumn or param type
	if (!pFX->IsValidOperation())
		return;

	switch (pFX->m_nOperation)
	{
	case CDaoFieldExchange::BindField:
		{
			LPDAOCOLUMNBINDING pcb =
				&pFX->m_prs->m_prgDaoColBindInfo[pFX->m_nField-1];
			pcb->dwDataType = DAO_DATE;
			pcb->dwBinding = DAOBINDING_DIRECT;
			pcb->cbDataOffset = (DWORD)&value.m_dt;
			pcb->dwUser = 0;
			pcb->cbMaxLen = sizeof(DATE);

			pFX->m_prs->m_cbFixedLengthFields += pcb->cbMaxLen;

			// Finish setting up column binding struct
			pFX->Default(lpszName,(void*)&value, AFX_RFX_DATE,
				dwBindOptions);
			return;
		}

	case CDaoFieldExchange::Fixup:
		// Must reset the valid currency flag
		if (pFX->m_prs->GetFieldLength(pFX->m_nField-1) == DAO_NULL)
			value.SetStatus(COleDateTime::null);
		else
			value.SetStatus(COleDateTime::valid);

		// Fall through to reset the NULL status

	default:
		pFX->Default(lpszName, (void*)&value, AFX_RFX_DATE,
			dwBindOptions);
		return;

#ifdef _DEBUG
	case CDaoFieldExchange::DumpField:
		*pFX->m_pdcDump << "\n" << lpszName << ":" << value;
		return;
#endif //_DEBUG
	}
}

//////////////////////////////////////////////////////////////////////////////
// DAO memory allocation callback helpers

STDAPI DaoStringAllocCallback(DWORD dwLen, DWORD pData, void** ppv)
{
	LPTSTR lpsz;
	CString* pstr = (CString*)pData;

#ifndef _UNICODE
	// If using ANSI DAO interfaces, DAO reports field length
	// rather than data length. In this case there will not be space
	// for NULL terminator if data length equals field length. Make room.
	dwLen++;
#endif

	TRY
	{
		// Only re-allocate if necessary
		lpsz = pstr->GetBuffer(dwLen);
		pstr->ReleaseBuffer(dwLen);
		*ppv = (void*)(dwLen > 0 ? lpsz : NULL);
	}
	CATCH_ALL(e)
	{
		e->Delete();
		return E_OUTOFMEMORY;
	}
	END_CATCH_ALL

	return S_OK;
}


STDAPI DaoBinaryAllocCallback(DWORD dwLen, DWORD pData, void** ppv)
{
	CByteArray* pByteArray = (CByteArray*)pData;

	TRY
	{
		// Only re-allocate if necessary
		pByteArray->SetSize(dwLen);
		*ppv = (void*)(dwLen > 0 ? &((*pByteArray)[0]) : NULL);
	}
	CATCH_ALL(e)
	{
		// Only exceptions thrown should be CMemoryException
		e->Delete();
		return E_OUTOFMEMORY;
	}
	END_CATCH_ALL

	return S_OK;
}


STDAPI DaoLongBinaryAllocCallback(DWORD dwLen, DWORD pData, void** ppv)
{
	CLongBinary* pLongBinary = (CLongBinary*)pData;

	TRY
	{
		AllocLongBinary(*pLongBinary, dwLen);
	}
	CATCH_ALL(e)
	{
		// Only exception is memory exception, just pass back error.
		DELETE_EXCEPTION(e);
		return E_OUTOFMEMORY;
	}
	END_CATCH_ALL

	if (pLongBinary->m_dwDataLength != 0)
	{
		const BYTE* pByte;
		pByte = (const BYTE*)::GlobalLock(pLongBinary->m_hData);

		// If mem can't be locked, free up and return error
		if (pByte == NULL)
		{
			::GlobalFree(pLongBinary->m_hData);
			pLongBinary->m_hData = NULL;
			return E_OUTOFMEMORY;
		}

		*ppv = (void*)pByte;
	}
	else
		*ppv = NULL;

	return S_OK;
}

void AFX_CDECL AllocLongBinary(CLongBinary& lb, DWORD dwDataLength)
{
	if (lb.m_hData == NULL)
	{
		if (dwDataLength > 0)
		{
			// Alloc memory, return error if not possible
			lb.m_hData = ::GlobalAlloc(GMEM_MOVEABLE, dwDataLength);
			if (lb.m_hData == NULL)
				AfxThrowMemoryException();
		}
	}
	else if (::GlobalSize(lb.m_hData) < dwDataLength)
	{
		// Alloc more mem, free up mem and throw exception if not possible
		if (::GlobalReAlloc(lb.m_hData, dwDataLength, 0) == NULL)
		{
			::GlobalFree(lb.m_hData);
			AfxThrowMemoryException();
		}
	}
	lb.m_dwDataLength = dwDataLength;
}

//////////////////////////////////////////////////////////////////////////////
// Inline function declarations expanded out-of-line

#ifndef _AFX_ENABLE_INLINES

static char _szAfxDaoInl[] = "afxdao.inl";
#undef THIS_FILE
#define THIS_FILE _szAfxDaoInl
#define _AFXDAODFX_INLINE
#include "afxdao.inl"

#endif

#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif

/////////////////////////////////////////////////////////////////////////////
