mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2709 lines
66 KiB
2709 lines
66 KiB
// 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"
|
|
|
|
#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
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDBByteArray db specific class for holding byte array data
|
|
class CDBByteArray : public CByteArray
|
|
{
|
|
DECLARE_DYNAMIC(CDBByteArray)
|
|
|
|
// Operations
|
|
void SetLength(int nNewSize);
|
|
};
|
|
|
|
inline void CDBByteArray::SetLength(int nNewSize)
|
|
{
|
|
// Can't grow buffer since ODBC has been SQLBindCol'd on it.
|
|
ASSERT(nNewSize <= m_nMaxSize);
|
|
m_nSize = nNewSize;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CFieldExchange
|
|
|
|
CFieldExchange::CFieldExchange(UINT nOperation, CRecordset* prs, void* pvField)
|
|
{
|
|
ASSERT(nOperation >= BindParam && nOperation <= DumpField);
|
|
ASSERT_VALID(prs);
|
|
ASSERT(prs->m_hstmt != SQL_NULL_HSTMT);
|
|
|
|
m_nFieldType = noFieldType;
|
|
m_nOperation = nOperation;
|
|
m_prs = prs;
|
|
m_pvField = pvField;
|
|
|
|
m_nFields = 0;
|
|
m_nParams = 0;
|
|
m_nParamFields = 0;
|
|
m_bField = FALSE;
|
|
m_pstr = NULL;
|
|
m_hstmt = SQL_NULL_HSTMT;
|
|
|
|
m_lDefaultLBFetchSize = 0x00010000;
|
|
m_lDefaultLBReallocSize = 0x00010000;
|
|
}
|
|
|
|
BOOL CFieldExchange::IsFieldType(UINT* pnField)
|
|
{
|
|
if (m_nFieldType == outputColumn)
|
|
{
|
|
*pnField = ++m_nFields;
|
|
// Recordset's m_nFields must match number of Fields!
|
|
ASSERT(m_nFields <= m_prs->m_nFields);
|
|
}
|
|
else
|
|
{
|
|
// Make sure SetFieldType was called
|
|
ASSERT(m_nFieldType == inputParam ||
|
|
m_nFieldType == outputParam ||
|
|
m_nFieldType == inoutParam);
|
|
|
|
*pnField = ++m_nParams;
|
|
// Recordset's m_nParams must match number of Params!
|
|
ASSERT(m_nParams <= m_prs->m_nParams);
|
|
}
|
|
|
|
if (m_nOperation == BindParam || m_nOperation == RebindParam)
|
|
{
|
|
// only valid on a param field type
|
|
return m_nFieldType != outputColumn;
|
|
}
|
|
else
|
|
{
|
|
// valid only on an outputColumn field type
|
|
return m_nFieldType == outputColumn;
|
|
}
|
|
}
|
|
|
|
// Default implementation for RFX functions
|
|
void CFieldExchange::Default(LPCTSTR szName,
|
|
void* pv, LONG* plLength, int nCType, UINT cbValue, UINT cbPrecision)
|
|
{
|
|
RETCODE nRetCode;
|
|
UINT nField = (m_nFieldType == outputColumn)? m_nFields: m_nParams;
|
|
switch (m_nOperation)
|
|
{
|
|
case BindParam:
|
|
if (m_prs->IsParamStatusNull(nField - 1))
|
|
*plLength = SQL_NULL_DATA;
|
|
else
|
|
*plLength = cbValue;
|
|
// For params, CType is same as SQL type
|
|
AFX_SQL_SYNC(::SQLBindParameter(m_hstmt, (UWORD)nField,
|
|
m_nFieldType, (SWORD)nCType, (SWORD)nCType, cbPrecision, 0,
|
|
pv, 0, plLength));
|
|
if (nRetCode != SQL_SUCCESS)
|
|
m_prs->ThrowDBException(nRetCode, m_hstmt);
|
|
|
|
// Add the member address to the param map
|
|
m_prs->m_mapParamIndex.SetAt(pv, (void*)nField);
|
|
return;
|
|
|
|
case RebindParam:
|
|
// Only need for date/time parameters and UNICODE text
|
|
return;
|
|
|
|
case BindFieldForUpdate:
|
|
if (!m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
// If not dirty, set length to SQL_IGNORE for SQLSetPos updates
|
|
*plLength = SQL_IGNORE;
|
|
}
|
|
else if (!m_prs->IsFieldStatusNull(nField - 1))
|
|
{
|
|
// Reset the length as it may have changed for var length fields
|
|
*plLength = cbValue;
|
|
}
|
|
return;
|
|
|
|
|
|
case UnbindFieldForUpdate:
|
|
// Reset bound length to actual length to clear SQL_IGNOREs
|
|
if (!m_prs->IsFieldStatusDirty(nField - 1))
|
|
*plLength = cbValue;
|
|
return;
|
|
|
|
case BindFieldToColumn:
|
|
AFX_SQL_SYNC(::SQLBindCol(m_prs->m_hstmt, (UWORD)nField, (SWORD)nCType,
|
|
pv, cbValue, plLength));
|
|
if (!m_prs->Check(nRetCode))
|
|
m_prs->ThrowDBException(nRetCode);
|
|
|
|
// Add the member address to the field map
|
|
m_prs->m_mapFieldIndex.SetAt(pv, (void*)nField);
|
|
return;
|
|
|
|
case Name:
|
|
if (m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
// We require a name
|
|
ASSERT(lstrlen(szName) != 0);
|
|
|
|
*m_pstr += szName;
|
|
*m_pstr += m_lpszSeparator;
|
|
}
|
|
return;
|
|
|
|
case NameValue:
|
|
if (m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
*m_pstr += szName;
|
|
*m_pstr += '=';
|
|
}
|
|
|
|
// Fall through
|
|
case Value:
|
|
if (m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
// If user marked column NULL, reflect this in length
|
|
if (m_prs->IsFieldStatusNull(nField - 1))
|
|
*plLength = SQL_NULL_DATA;
|
|
else
|
|
*plLength = cbValue;
|
|
|
|
// If optimizing for bulk add, only need lengths set correctly
|
|
if(!(m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
|
|
{
|
|
*m_pstr += '?';
|
|
*m_pstr += m_lpszSeparator;
|
|
m_nParamFields++;
|
|
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
AFX_SQL_SYNC(::SQLBindParameter(m_hstmt,
|
|
(UWORD)m_nParamFields, SQL_PARAM_INPUT,
|
|
(SWORD)nCType, pODBCInfo->m_nSQLType,
|
|
pODBCInfo->m_nPrecision, pODBCInfo->m_nScale,
|
|
pv, 0, plLength));
|
|
if (nRetCode != SQL_SUCCESS)
|
|
m_prs->ThrowDBException(nRetCode, m_hstmt);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case MarkForUpdate:
|
|
{
|
|
// Get the field data
|
|
CFieldInfo* pInfo = &m_prs->m_rgFieldInfos[nField - 1];
|
|
|
|
// If user changed field value from previous value, mark field dirty
|
|
if ((pInfo->m_bStatus & AFX_SQL_FIELD_FLAG_NULL))
|
|
{
|
|
if (!m_prs->IsFieldStatusNull(nField - 1))
|
|
m_prs->SetDirtyFieldStatus(nField - 1);
|
|
}
|
|
else
|
|
{
|
|
// Saved field is not NULL. current field null, so field dirty
|
|
BOOL bDirty = m_prs->IsFieldStatusNull(nField - 1);
|
|
|
|
// If values differ, then field dirty
|
|
void* pvDataCache;
|
|
|
|
if (pInfo->m_nDataType == AFX_RFX_BOOL ||
|
|
pInfo->m_nDataType == AFX_RFX_BYTE ||
|
|
pInfo->m_nDataType == AFX_RFX_INT ||
|
|
pInfo->m_nDataType == AFX_RFX_LONG ||
|
|
pInfo->m_nDataType == AFX_RFX_SINGLE)
|
|
{
|
|
// If caching data by value, pass a ref
|
|
pvDataCache = &pInfo->m_pvDataCache;
|
|
}
|
|
else
|
|
pvDataCache = pInfo->m_pvDataCache;
|
|
|
|
if (bDirty || !AfxCompareValueByRef(pv, pvDataCache, pInfo->m_nDataType))
|
|
m_prs->SetDirtyFieldStatus(nField - 1);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
// Field address must not change - ODBC's SQLBindCol depends upon this
|
|
void* pvBind;
|
|
|
|
switch (pInfo->m_nDataType)
|
|
{
|
|
default:
|
|
pvBind = pv;
|
|
break;
|
|
|
|
case AFX_RFX_TEXT:
|
|
#ifdef _UNICODE
|
|
pvBind = m_prs->m_pvFieldProxy[nField-1];
|
|
#else // !_UNICODE
|
|
{
|
|
pvBind = ((CString*)pv)->GetBuffer(0);
|
|
((CString*)pv)->ReleaseBuffer();
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case AFX_RFX_DATE:
|
|
pvBind = m_prs->m_pvFieldProxy[nField-1];
|
|
break;
|
|
}
|
|
|
|
if (pInfo->m_pvBindAddress != pvBind)
|
|
{
|
|
TRACE1("Error: field address (column %u) has changed!\n",
|
|
nField);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
if ((m_pvField == NULL || m_pvField == pv) &&
|
|
m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
m_bField = TRUE;
|
|
}
|
|
}
|
|
return;
|
|
|
|
case StoreField:
|
|
AfxStoreField(*m_prs, nField, pv);
|
|
return;
|
|
|
|
case LoadField:
|
|
AfxLoadField(*m_prs, nField, pv, plLength);
|
|
return;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
// Note: CString.m_pchData must not be changed. This address is registered
|
|
// with ODBC and must remain valid until the recordset is released.
|
|
void AFXAPI RFX_Text(CFieldExchange* pFX, LPCTSTR szName,
|
|
CString& value, int nMaxLength, int nColumnType, short nScale)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
ASSERT(AfxIsValidAddress(&value, sizeof(CString)));
|
|
|
|
RETCODE nRetCode;
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
default:
|
|
pFX->Default(szName, value.GetBuffer(0), plLength,
|
|
SQL_C_CHAR, value.GetLength(), nMaxLength);
|
|
value.ReleaseBuffer();
|
|
return;
|
|
|
|
case CFieldExchange::BindParam:
|
|
{
|
|
// Preallocate to nMaxLength and setup binding address
|
|
value.GetBufferSetLength(nMaxLength);
|
|
void* pvParam = value.LockBuffer(); // will be overwritten if UNICODE
|
|
|
|
#ifdef _UNICODE
|
|
// Must use proxy to translate unicode data into non-unicode param
|
|
pFX->m_prs->m_bRebindParams = TRUE;
|
|
|
|
// Allocate proxy array if necessary
|
|
if (pFX->m_prs->m_pvParamProxy == NULL)
|
|
{
|
|
pFX->m_prs->m_pvParamProxy = new void*[pFX->m_prs->m_nParams];
|
|
memset(pFX->m_prs->m_pvParamProxy, 0,
|
|
pFX->m_prs->m_nParams*sizeof(void*));
|
|
pFX->m_prs->m_nProxyParams = pFX->m_prs->m_nParams;
|
|
}
|
|
|
|
// Allocate non-unicode string to nMaxLength if necessary for SQLBindParameter
|
|
if (pFX->m_prs->m_pvParamProxy[nField-1] == NULL)
|
|
{
|
|
pvParam = new CHAR[nMaxLength+1];
|
|
pFX->m_prs->m_pvParamProxy[nField-1] = pvParam;
|
|
}
|
|
else
|
|
pvParam = pFX->m_prs->m_pvParamProxy[nField-1];
|
|
|
|
// Now fill in the data value
|
|
USES_CONVERSION;
|
|
lstrcpyA((char*)pvParam, T2A((LPCTSTR)value));
|
|
#endif // _UNICODE
|
|
|
|
*plLength = SQL_NTS;
|
|
AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt, (UWORD)nField,
|
|
pFX->m_nFieldType, SQL_C_CHAR, (SWORD)nColumnType,
|
|
nMaxLength, nScale, pvParam, 0, plLength));
|
|
|
|
value.ReleaseBuffer();
|
|
|
|
if (nRetCode != SQL_SUCCESS)
|
|
pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
|
|
|
|
// Add the member address to the param map
|
|
pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
|
|
}
|
|
return;
|
|
|
|
#ifdef _UNICODE
|
|
case CFieldExchange::RebindParam:
|
|
if (pFX->m_prs->m_nProxyParams != 0)
|
|
{
|
|
// Fill buffer (expected by SQLBindParameter) with new param data
|
|
USES_CONVERSION;
|
|
LPSTR lpszParam = (LPSTR)pFX->m_prs->m_pvParamProxy[nField-1];
|
|
lstrcpyA(lpszParam, T2A((LPCTSTR)value));
|
|
}
|
|
return;
|
|
#endif // _UNICODE
|
|
|
|
case CFieldExchange::BindFieldToColumn:
|
|
{
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
UINT cbColumn = pODBCInfo->m_nPrecision;
|
|
|
|
switch (pODBCInfo->m_nSQLType)
|
|
{
|
|
default:
|
|
#ifdef _DEBUG
|
|
// Warn of possible field schema mismatch
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE1("Warning: CString converted from SQL type %ld.\n",
|
|
pODBCInfo->m_nSQLType);
|
|
#endif // _DEBUG
|
|
break;
|
|
|
|
case SQL_LONGVARCHAR:
|
|
case SQL_CHAR:
|
|
case SQL_VARCHAR:
|
|
break;
|
|
|
|
case SQL_DECIMAL:
|
|
case SQL_NUMERIC:
|
|
// Add room for sign and decimal point
|
|
cbColumn += 2;
|
|
break;
|
|
|
|
case SQL_TIMESTAMP:
|
|
case SQL_DATE:
|
|
case SQL_TIME:
|
|
// May need extra space, i.e. "{TS mm/dd/yyyy hh:mm:ss}"
|
|
cbColumn += 10;
|
|
break;
|
|
|
|
case SQL_BIGINT:
|
|
// Add room for sign
|
|
cbColumn += 1;
|
|
break;
|
|
}
|
|
|
|
// Constrain to user specified max length
|
|
if (cbColumn > (UINT)nMaxLength)
|
|
cbColumn = nMaxLength;
|
|
|
|
// Set up binding addres
|
|
void* pvData;
|
|
value.GetBufferSetLength(cbColumn+1);
|
|
pvData = value.LockBuffer(); // will be overwritten if UNICODE
|
|
|
|
#ifdef _UNICODE
|
|
// Allocate proxy array if necessary
|
|
if (pFX->m_prs->m_pvFieldProxy == NULL)
|
|
{
|
|
pFX->m_prs->m_pvFieldProxy = new void*[pFX->m_prs->m_nFields];
|
|
memset(pFX->m_prs->m_pvFieldProxy, 0,
|
|
pFX->m_prs->m_nFields*sizeof(void*));
|
|
pFX->m_prs->m_nProxyFields = pFX->m_prs->m_nFields;
|
|
}
|
|
|
|
// Allocate non-unicode string for SQLBindCol (not necessary on Requery)
|
|
if (pFX->m_prs->m_pvFieldProxy[nField-1] == NULL)
|
|
pFX->m_prs->m_pvFieldProxy[nField-1] = new CHAR[cbColumn+2];
|
|
|
|
pvData = pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
#endif // _UNICODE
|
|
|
|
AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
|
|
SQL_C_CHAR, pvData, cbColumn+1, plLength));
|
|
value.ReleaseBuffer();
|
|
if (!pFX->m_prs->Check(nRetCode))
|
|
pFX->m_prs->ThrowDBException(nRetCode);
|
|
|
|
// Add the member address to the field map
|
|
pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
|
|
}
|
|
return;
|
|
|
|
#ifdef _UNICODE
|
|
case CFieldExchange::BindFieldForUpdate:
|
|
if (pFX->m_prs->m_nProxyFields != 0)
|
|
{
|
|
// Fill buffer (expected by SQLSetPos) with new field data
|
|
USES_CONVERSION;
|
|
LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
lstrcpyA(lpszData, T2A((LPCTSTR)value));
|
|
|
|
pFX->Default(szName, (void *)lpszData, plLength, SQL_C_CHAR,
|
|
value.GetLength(), nMaxLength);
|
|
}
|
|
return;
|
|
#endif // _UNICODE
|
|
|
|
case CFieldExchange::Fixup:
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value.GetBufferSetLength(0);
|
|
value.ReleaseBuffer();
|
|
}
|
|
else
|
|
{
|
|
#ifdef _UNICODE
|
|
// Copy value out of the proxy
|
|
value = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
#endif
|
|
|
|
LPTSTR lpsz = value.GetBuffer(0);
|
|
if (pFX->m_prs->m_pDatabase->m_bStripTrailingSpaces)
|
|
{
|
|
// find first trailing space
|
|
LPTSTR lpszFirstTrailing = NULL;
|
|
while (*lpsz != '\0')
|
|
{
|
|
if (*lpsz != ' ')
|
|
lpszFirstTrailing = NULL;
|
|
else
|
|
{
|
|
if (lpszFirstTrailing == NULL)
|
|
lpszFirstTrailing = lpsz;
|
|
}
|
|
lpsz = _tcsinc(lpsz);
|
|
}
|
|
// truncate
|
|
if (lpszFirstTrailing != NULL)
|
|
*lpszFirstTrailing = '\0';
|
|
|
|
}
|
|
value.ReleaseBuffer();
|
|
*plLength = value.GetLength();
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
// Set string 0 length
|
|
value.GetBufferSetLength(0);
|
|
value.ReleaseBuffer();
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
*plLength = SQL_NTS;
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
|
|
#ifdef _UNICODE
|
|
case CFieldExchange::NameValue:
|
|
if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
*pFX->m_pstr += szName;
|
|
*pFX->m_pstr += '=';
|
|
}
|
|
// Fall through
|
|
|
|
case CFieldExchange::Value:
|
|
if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
// Get the field data
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
if (pFX->m_prs->IsFieldStatusNull(nField - 1))
|
|
{
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
USES_CONVERSION;
|
|
lstrcpyA(lpszData, T2A((LPCTSTR)value));
|
|
*plLength = value.GetLength();
|
|
}
|
|
|
|
// If optimizing for bulk add, only need lengths & proxy set correctly
|
|
if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
|
|
{
|
|
*pFX->m_pstr += '?';
|
|
*pFX->m_pstr += pFX->m_lpszSeparator;
|
|
pFX->m_nParamFields++;
|
|
AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
|
|
(UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
|
|
SQL_C_CHAR, pODBCInfo->m_nSQLType, nMaxLength, 0,
|
|
lpszData, 0, plLength));
|
|
|
|
// Add the member address to the param map
|
|
pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
|
|
}
|
|
}
|
|
return;
|
|
#endif // _UNICODE
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
// can force writing of psuedo-null value (as a non-null) by setting field dirty
|
|
if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
if (!value.IsEmpty())
|
|
{
|
|
pFX->m_prs->SetDirtyFieldStatus(nField - 1);
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForUpdate:
|
|
if (value.IsEmpty())
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
else
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
pFX->Default(szName, &value, plLength,
|
|
SQL_C_CHAR, value.GetLength(), nMaxLength);
|
|
return;
|
|
|
|
case CFieldExchange::LoadField:
|
|
{
|
|
// Get the field data
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
CString* pStrCachedValue = (CString*)pInfo->m_pvDataCache;
|
|
|
|
// Restore the status
|
|
pFX->m_prs->SetFieldStatus(nField - 1, pInfo->m_bStatus);
|
|
|
|
// If not NULL, restore the value and length
|
|
if (!pFX->m_prs->IsFieldStatusNull(nField - 1))
|
|
{
|
|
value = *pStrCachedValue;
|
|
*plLength = value.GetLength();
|
|
|
|
#ifdef _UNICODE
|
|
// Must restore proxy for correct WHERE CURRENT OF operation
|
|
USES_CONVERSION;
|
|
LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
lstrcpyA(lpszData, T2A((LPCTSTR)value));
|
|
#endif // _UNICODE
|
|
}
|
|
else
|
|
{
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
// Buffer address must not change - ODBC's SQLBindCol depends upon this
|
|
void* pvBind;
|
|
|
|
#ifdef _UNICODE
|
|
pvBind = pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
#else // !_UNICODE
|
|
pvBind = value.GetBuffer(0);
|
|
value.ReleaseBuffer();
|
|
#endif
|
|
|
|
if (pvBind != pInfo->m_pvBindAddress)
|
|
{
|
|
TRACE1("Error: CString buffer (column %u) address has changed!\n",
|
|
nField);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::StoreField:
|
|
AfxStoreField(*pFX->m_prs, nField, &value);
|
|
return;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
{
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
pInfo->m_pvDataCache = new CString;
|
|
pInfo->m_nDataType = AFX_RFX_TEXT;
|
|
}
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << " = " << value;
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Int(CFieldExchange* pFX, LPCTSTR szName, int& value)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::BindFieldToColumn:
|
|
{
|
|
#ifdef _DEBUG
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
if (pODBCInfo->m_nSQLType != SQL_C_SHORT)
|
|
{
|
|
// Warn of possible field schema mismatch
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE1("Warning: int converted from SQL type %ld.\n",
|
|
pODBCInfo->m_nSQLType);
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
// fall through
|
|
|
|
default:
|
|
LDefault:
|
|
pFX->Default(szName, &value, plLength, SQL_C_LONG,
|
|
sizeof(value), 5);
|
|
return;
|
|
|
|
case CFieldExchange::Fixup:
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_INT_PSEUDO_NULL;
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_INT_PSEUDO_NULL;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
*plLength = sizeof(value);
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
// can force writing of psuedo-null value (as a non-null) by setting field dirty
|
|
if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
if (value != AFX_RFX_INT_PSEUDO_NULL)
|
|
{
|
|
pFX->m_prs->SetDirtyFieldStatus(nField - 1);
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForUpdate:
|
|
if (value != AFX_RFX_INT_PSEUDO_NULL)
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
goto LDefault;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
{
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
|
|
// Data cached by value, no allocation necessary
|
|
pInfo->m_nDataType = AFX_RFX_INT;
|
|
}
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << " = " << value;
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Long(CFieldExchange* pFX, LPCTSTR szName, long& value)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::BindFieldToColumn:
|
|
{
|
|
#ifdef _DEBUG
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
if (pODBCInfo->m_nSQLType != SQL_C_LONG)
|
|
{
|
|
// Warn of possible field schema mismatch
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE1("Warning: long converted from SQL type %ld.\n",
|
|
pODBCInfo->m_nSQLType);
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
// fall through
|
|
|
|
default:
|
|
LDefault:
|
|
pFX->Default(szName, &value, plLength, SQL_C_LONG,
|
|
sizeof(value), 10);
|
|
return;
|
|
|
|
case CFieldExchange::Fixup:
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_LONG_PSEUDO_NULL;
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_LONG_PSEUDO_NULL;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
*plLength = sizeof(value);
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
// can force writing of psuedo-null value (as a non-null) by setting field dirty
|
|
if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
if (value != AFX_RFX_LONG_PSEUDO_NULL)
|
|
{
|
|
pFX->m_prs->SetDirtyFieldStatus(nField - 1);
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForUpdate:
|
|
if (value != AFX_RFX_LONG_PSEUDO_NULL)
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
goto LDefault;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
{
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
|
|
// Data cached by value, no allocation necessary
|
|
pInfo->m_nDataType = AFX_RFX_LONG;
|
|
}
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << " = " << value;
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Byte(CFieldExchange* pFX, LPCTSTR szName, BYTE& value)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::BindFieldToColumn:
|
|
{
|
|
#ifdef _DEBUG
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
if (pODBCInfo->m_nSQLType != SQL_TINYINT)
|
|
{
|
|
// Warn of possible field schema mismatch
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE1("Warning: BYTE converted from SQL type %ld.\n",
|
|
pODBCInfo->m_nSQLType);
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
// fall through
|
|
|
|
default:
|
|
LDefault:
|
|
pFX->Default(szName, &value, plLength, SQL_TINYINT,
|
|
sizeof(value), 3);
|
|
break;
|
|
|
|
case CFieldExchange::Fixup:
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_BYTE_PSEUDO_NULL;
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_BYTE_PSEUDO_NULL;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
*plLength = sizeof(value);
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
// can force writing of psuedo-null value (as a non-null) by setting field dirty
|
|
if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
if (value != AFX_RFX_BYTE_PSEUDO_NULL)
|
|
{
|
|
pFX->m_prs->SetDirtyFieldStatus(nField - 1);
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForUpdate:
|
|
if (value != AFX_RFX_BYTE_PSEUDO_NULL)
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
goto LDefault;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
{
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
|
|
// Data cached by value, no allocation necessary
|
|
pInfo->m_nDataType = AFX_RFX_BYTE;
|
|
}
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << " = " << value;
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Bool(CFieldExchange* pFX, LPCTSTR szName, BOOL& value)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::BindFieldToColumn:
|
|
{
|
|
#ifdef _DEBUG
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
if (pODBCInfo->m_nSQLType != SQL_BIT)
|
|
{
|
|
// Warn of possible field schema mismatch
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE1("Warning: BOOL converted from SQL type %ld.\n",
|
|
pODBCInfo->m_nSQLType);
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
// Fall through
|
|
|
|
default:
|
|
LDefault:
|
|
pFX->Default(szName, &value, plLength, SQL_BIT,
|
|
sizeof(value), 1);
|
|
return;
|
|
|
|
case CFieldExchange::Fixup:
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_BOOL_PSEUDO_NULL;
|
|
}
|
|
else
|
|
// Cast BYTE into BOOL (int)
|
|
value = *(BYTE *)&value;
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_BOOL_PSEUDO_NULL;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
*plLength = sizeof(value);
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
// can force writing of psuedo-null value (as a non-null) by setting field dirty
|
|
if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
if (value != AFX_RFX_BOOL_PSEUDO_NULL)
|
|
{
|
|
pFX->m_prs->SetDirtyFieldStatus(nField - 1);
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForUpdate:
|
|
if (value != AFX_RFX_BOOL_PSEUDO_NULL)
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
goto LDefault;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
{
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
|
|
// Data cached by value, no allocation necessary
|
|
pInfo->m_nDataType = AFX_RFX_BOOL;
|
|
}
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << " = " << value;
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
// Note: CByteArray.m_pData must not be changed. This address is registered
|
|
// with ODBC and must remain valid until the recordset is released.
|
|
void AFXAPI RFX_Binary(CFieldExchange* pFX, LPCTSTR szName,
|
|
CByteArray& value, int nMaxLength)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
RETCODE nRetCode;
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
default:
|
|
LDefault:
|
|
pFX->Default(szName,
|
|
(value.GetSize() > 0) ? &value[0] : NULL, plLength,
|
|
SQL_C_BINARY, (int)value.GetSize(), (UINT)value.GetSize());
|
|
return;
|
|
|
|
case CFieldExchange::BindFieldToColumn:
|
|
{
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
UDWORD cbColumn = pODBCInfo->m_nPrecision;
|
|
|
|
#ifdef _DEBUG
|
|
if (pODBCInfo->m_nSQLType != SQL_BINARY &&
|
|
pODBCInfo->m_nSQLType != SQL_VARBINARY &&
|
|
pODBCInfo->m_nSQLType != SQL_LONGVARBINARY)
|
|
{
|
|
// Warn of possible field schema mismatch
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE1("Warning: CByteArray converted from SQL type %ld.\n",
|
|
pODBCInfo->m_nSQLType);
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// Constrain to user specified max length
|
|
if (cbColumn > (UINT)nMaxLength)
|
|
cbColumn = nMaxLength;
|
|
value.SetSize(cbColumn);
|
|
AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
|
|
SQL_C_BINARY, &value[0], (LONG)cbColumn, plLength));
|
|
if (!pFX->m_prs->Check(nRetCode))
|
|
pFX->m_prs->ThrowDBException(nRetCode);
|
|
|
|
// Add the member address to the field map
|
|
pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::Fixup:
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value.SetSize(1);
|
|
value[0] = AFX_RFX_BYTE_PSEUDO_NULL;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(*plLength <= (LONG)nMaxLength);
|
|
((CDBByteArray&)value).SetLength((UINT)*plLength);
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value.SetSize(1);
|
|
value[0] = AFX_RFX_BYTE_PSEUDO_NULL;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
*plLength = value.GetSize();
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
// can force writing of psuedo-null value (as a non-null) by setting field dirty
|
|
if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
if (value.GetSize() != 1 || value[0] != AFX_RFX_BYTE_PSEUDO_NULL)
|
|
{
|
|
pFX->m_prs->SetDirtyFieldStatus(nField - 1);
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForUpdate:
|
|
if (value.GetSize() != 1 || value[0] != AFX_RFX_BYTE_PSEUDO_NULL)
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
goto LDefault;
|
|
|
|
case CFieldExchange::StoreField:
|
|
AfxStoreField(*pFX->m_prs, nField, &value);
|
|
return;
|
|
|
|
case CFieldExchange::LoadField:
|
|
AfxLoadField(*pFX->m_prs, nField, &value, plLength);
|
|
return;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
{
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
pInfo->m_pvDataCache = new CByteArray;
|
|
pInfo->m_nDataType = AFX_RFX_BINARY;
|
|
}
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << ":";
|
|
value.Dump(*pFX->m_pdcDump);
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Date(CFieldExchange* pFX, LPCTSTR szName, CTime& value)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
RETCODE nRetCode;
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
default:
|
|
LDefault:
|
|
pFX->Default(szName, &value, plLength, SQL_C_TIMESTAMP,
|
|
sizeof(value), TIMESTAMP_PRECISION);
|
|
return;
|
|
|
|
case CFieldExchange::BindParam:
|
|
{
|
|
TIMESTAMP_STRUCT* pts;
|
|
pFX->m_prs->m_bRebindParams = TRUE;
|
|
|
|
if (pFX->m_prs->IsParamStatusNull(nField - 1))
|
|
{
|
|
pts = NULL;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
// Allocate proxy array if necessary
|
|
if (pFX->m_prs->m_pvParamProxy == NULL)
|
|
{
|
|
pFX->m_prs->m_pvParamProxy = new void*[pFX->m_prs->m_nParams];
|
|
memset(pFX->m_prs->m_pvParamProxy, 0,
|
|
pFX->m_prs->m_nParams*sizeof(void*));
|
|
pFX->m_prs->m_nProxyParams = pFX->m_prs->m_nParams;
|
|
}
|
|
|
|
// Allocate TIMESTAMP_STRUCT if necessary for SQLBindParameter
|
|
if (pFX->m_prs->m_pvParamProxy[nField-1] == NULL)
|
|
{
|
|
pts = new TIMESTAMP_STRUCT;
|
|
pFX->m_prs->m_pvParamProxy[nField-1] = pts;
|
|
}
|
|
else
|
|
pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvParamProxy[nField-1];
|
|
|
|
pts->year = (SWORD)value.GetYear();
|
|
pts->month = (UWORD)value.GetMonth();
|
|
pts->day = (UWORD)value.GetDay();
|
|
pts->hour = (UWORD)value.GetHour();
|
|
pts->minute = (UWORD)value.GetMinute();
|
|
pts->second = (UWORD)value.GetSecond();
|
|
pts->fraction = 0;
|
|
*plLength = sizeof(TIMESTAMP_STRUCT);
|
|
}
|
|
|
|
AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt, (UWORD)nField,
|
|
pFX->m_nFieldType, SQL_C_TIMESTAMP, SQL_C_TIMESTAMP,
|
|
TIMESTAMP_PRECISION, 0, pts, 0, plLength));
|
|
if (nRetCode != SQL_SUCCESS)
|
|
pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
|
|
|
|
// Add the member address to the param map
|
|
pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::RebindParam:
|
|
{
|
|
if (pFX->m_prs->m_nProxyParams != 0)
|
|
{
|
|
// Fill buffer (expected by SQLBindParameter) with new param data
|
|
TIMESTAMP_STRUCT* pts;
|
|
pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvParamProxy[nField-1];
|
|
pts->year = (SWORD)value.GetYear();
|
|
pts->month = (UWORD)value.GetMonth();
|
|
pts->day = (UWORD)value.GetDay();
|
|
pts->hour = (UWORD)value.GetHour();
|
|
pts->minute = (UWORD)value.GetMinute();
|
|
pts->second = (UWORD)value.GetSecond();
|
|
pts->fraction = 0;
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::BindFieldToColumn:
|
|
{
|
|
#ifdef _DEBUG
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
if (pODBCInfo->m_nSQLType != SQL_DATE &&
|
|
pODBCInfo->m_nSQLType != SQL_TIME &&
|
|
pODBCInfo->m_nSQLType != SQL_TIMESTAMP)
|
|
{
|
|
// Warn of possible field schema mismatch
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE1("Warning: CTime converted from SQL type %ld.\n",
|
|
pODBCInfo->m_nSQLType);
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// Allocate proxy array if necessary
|
|
if (pFX->m_prs->m_pvFieldProxy == NULL)
|
|
{
|
|
pFX->m_prs->m_pvFieldProxy = new void*[pFX->m_prs->m_nFields];
|
|
memset(pFX->m_prs->m_pvFieldProxy, 0,
|
|
pFX->m_prs->m_nFields*sizeof(void*));
|
|
pFX->m_prs->m_nProxyFields = pFX->m_prs->m_nFields;
|
|
}
|
|
|
|
// Allocate TIMESTAMP_STRUCT for SQLBindCol (not necessary on Requery)
|
|
if (pFX->m_prs->m_pvFieldProxy[nField-1] == NULL)
|
|
pFX->m_prs->m_pvFieldProxy[nField-1] = new TIMESTAMP_STRUCT;
|
|
|
|
AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
|
|
SQL_C_TIMESTAMP, pFX->m_prs->m_pvFieldProxy[nField-1],
|
|
sizeof(TIMESTAMP_STRUCT), plLength));
|
|
if (!pFX->m_prs->Check(nRetCode))
|
|
pFX->m_prs->ThrowDBException(nRetCode);
|
|
|
|
// Add the member address to the field map
|
|
pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::BindFieldForUpdate:
|
|
if (pFX->m_prs->m_nProxyFields != 0)
|
|
{
|
|
// Fill buffer (expected by SQLSetPos) with new field data
|
|
TIMESTAMP_STRUCT* pts;
|
|
pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
pts->year = (SWORD)value.GetYear();
|
|
pts->month = (UWORD)value.GetMonth();
|
|
pts->day = (UWORD)value.GetDay();
|
|
pts->hour = (UWORD)value.GetHour();
|
|
pts->minute = (UWORD)value.GetMinute();
|
|
pts->second = (UWORD)value.GetSecond();
|
|
pts->fraction = 0;
|
|
|
|
pFX->Default(szName, (void *)pts, plLength, SQL_C_TIMESTAMP,
|
|
sizeof(TIMESTAMP_STRUCT), TIMESTAMP_PRECISION);
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::Fixup:
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_DATE_PSEUDO_NULL;
|
|
}
|
|
else
|
|
{
|
|
TIMESTAMP_STRUCT* pts =
|
|
(TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
if (pts->year < 1970 || pts->year > 2038)
|
|
{
|
|
// Time value out of range, return NULL
|
|
#ifdef _DEBUG
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE0("Warning: date value out of range, returning NULL value.\n");
|
|
#endif
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_DATE_PSEUDO_NULL;
|
|
}
|
|
else
|
|
{
|
|
#ifdef _DEBUG
|
|
if ((afxTraceFlags & traceDatabase) && pts->fraction != 0)
|
|
TRACE0("Warning: ignoring milliseconds.\n");
|
|
#endif
|
|
value = CTime(pts->year, pts->month, pts->day,
|
|
pts->hour, pts->minute, pts->second);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::NameValue:
|
|
if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
*pFX->m_pstr += szName;
|
|
*pFX->m_pstr += '=';
|
|
}
|
|
// Fall through
|
|
|
|
case CFieldExchange::Value:
|
|
if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
TIMESTAMP_STRUCT* pts =
|
|
(TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
if (pFX->m_prs->IsFieldStatusNull(nField - 1))
|
|
{
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pts->year = (SWORD)value.GetYear();
|
|
pts->month = (UWORD)value.GetMonth();
|
|
pts->day = (UWORD)value.GetDay();
|
|
pts->hour = (UWORD)value.GetHour();
|
|
pts->minute = (UWORD)value.GetMinute();
|
|
pts->second = (UWORD)value.GetSecond();
|
|
pts->fraction = 0;
|
|
*plLength = sizeof(TIMESTAMP_STRUCT);
|
|
}
|
|
|
|
// If optimizing for bulk add, only need lengths & proxy set correctly
|
|
if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
|
|
{
|
|
*pFX->m_pstr += '?';
|
|
*pFX->m_pstr += pFX->m_lpszSeparator;
|
|
pFX->m_nParamFields++;
|
|
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
|
|
(UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
|
|
SQL_C_TIMESTAMP, pODBCInfo->m_nSQLType,
|
|
TIMESTAMP_PRECISION, 0, pts, 0, plLength));
|
|
|
|
// Add the member address to the param map
|
|
pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value = AFX_RFX_DATE_PSEUDO_NULL;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
*plLength = sizeof(TIMESTAMP_STRUCT);
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
// can force writing of psuedo-null value (as a non-null) by setting field dirty
|
|
if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
CTime timeNull = AFX_RFX_DATE_PSEUDO_NULL;
|
|
if (value != timeNull)
|
|
{
|
|
pFX->m_prs->SetDirtyFieldStatus(nField - 1);
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForUpdate:
|
|
{
|
|
CTime timeNull = AFX_RFX_DATE_PSEUDO_NULL;
|
|
if (value != timeNull)
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
goto LDefault;
|
|
|
|
case CFieldExchange::LoadField:
|
|
{
|
|
// Get the field data
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
|
|
// Restore the status
|
|
pFX->m_prs->SetFieldStatus(nField - 1, pInfo->m_bStatus);
|
|
|
|
// If not NULL, restore the value, length and proxy
|
|
if (!pFX->m_prs->IsFieldStatusNull(nField - 1))
|
|
{
|
|
AfxCopyValueByRef(pInfo->m_pvDataCache, &value,
|
|
plLength, pInfo->m_nDataType);
|
|
|
|
// Restore proxy for correct WHERE CURRENT OF operations
|
|
TIMESTAMP_STRUCT* pts =
|
|
(TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
|
|
|
|
pts->year = (SWORD)value.GetYear();
|
|
pts->month = (UWORD)value.GetMonth();
|
|
pts->day = (UWORD)value.GetDay();
|
|
pts->hour = (UWORD)value.GetHour();
|
|
pts->minute = (UWORD)value.GetMinute();
|
|
pts->second = (UWORD)value.GetSecond();
|
|
pts->fraction = 0;
|
|
}
|
|
else
|
|
*plLength = SQL_NULL_DATA;
|
|
|
|
#ifdef _DEBUG
|
|
// Buffer address must not change - ODBC's SQLBindCol depends upon this
|
|
if (pInfo->m_pvBindAddress != pFX->m_prs->m_pvFieldProxy[nField-1])
|
|
{
|
|
TRACE1("Error: CString buffer (column %u) address has changed!\n",
|
|
nField);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
{
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
pInfo->m_pvDataCache = new CTime;
|
|
pInfo->m_nDataType = AFX_RFX_DATE;
|
|
}
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << " = " << value;
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Date(CFieldExchange* pFX, LPCTSTR szName,
|
|
TIMESTAMP_STRUCT& value)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::BindFieldToColumn:
|
|
{
|
|
#ifdef _DEBUG
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
if (pODBCInfo->m_nSQLType != SQL_DATE &&
|
|
pODBCInfo->m_nSQLType != SQL_TIME &&
|
|
pODBCInfo->m_nSQLType != SQL_TIMESTAMP)
|
|
{
|
|
// Warn of possible field schema mismatch
|
|
if (afxTraceFlags & traceDatabase)
|
|
TRACE1("Warning: TIMESTAMP_STRUCT converted from SQL type %ld.\n",
|
|
pODBCInfo->m_nSQLType);
|
|
}
|
|
#endif // _DEBUG
|
|
// fall through
|
|
}
|
|
|
|
default:
|
|
LDefault:
|
|
pFX->Default(szName, &value, plLength, SQL_C_TIMESTAMP,
|
|
sizeof(value), TIMESTAMP_PRECISION);
|
|
return;
|
|
|
|
case CFieldExchange::Fixup:
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value.year = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.month = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.day = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.hour = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.minute = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.second = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.fraction = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value.year = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.month = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.day = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.hour = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.minute = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.second = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
value.fraction = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
*plLength = sizeof(TIMESTAMP_STRUCT);
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
// can force writing of psuedo-null value (as a non-null) by setting field dirty
|
|
if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
if (!(value.year == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.month == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.day == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.hour == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.minute == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.second == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.fraction == AFX_RFX_TIMESTAMP_PSEUDO_NULL ))
|
|
{
|
|
pFX->m_prs->SetDirtyFieldStatus(nField - 1);
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::MarkForUpdate:
|
|
if (!(value.year == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.month == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.day == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.hour == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.minute == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.second == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
|
|
value.fraction == AFX_RFX_TIMESTAMP_PSEUDO_NULL ))
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
goto LDefault;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
{
|
|
CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
|
|
pInfo->m_pvDataCache = new TIMESTAMP_STRUCT;
|
|
pInfo->m_nDataType = AFX_RFX_TIMESTAMP;
|
|
}
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << ".year = " << (int)value.year;
|
|
*pFX->m_pdcDump << "\n" << szName << ".month = " << value.month;
|
|
*pFX->m_pdcDump << "\n" << szName << ".day = " << value.day;
|
|
*pFX->m_pdcDump << "\n" << szName << ".hour = " << value.hour;
|
|
*pFX->m_pdcDump << "\n" << szName << ".minute = " << value.minute;
|
|
*pFX->m_pdcDump << "\n" << szName << ".second = " << value.second;
|
|
*pFX->m_pdcDump << "\n" << szName << ".fraction = " << value.fraction;
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_LongBinary(CFieldExchange* pFX, LPCTSTR szName,
|
|
CLongBinary& value)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
RETCODE nRetCode;
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
|
|
nField - 1, pFX->m_nFieldType);
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::Name:
|
|
pFX->m_prs->m_bLongBinaryColumns = TRUE;
|
|
pFX->Default(szName, &value, plLength, SQL_C_DEFAULT, 0, 0);
|
|
return;
|
|
|
|
case CFieldExchange::BindFieldToColumn:
|
|
// Don't bind if using update SQL, simply do SQLGetData on Fixup
|
|
if (!pFX->m_prs->m_bUseUpdateSQL && pFX->m_prs->CanUpdate())
|
|
{
|
|
// Bind for updates with cb=0 now. Driver may not support post Execute or ExtendedFetch binding
|
|
AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField, SQL_C_DEFAULT,
|
|
&value, 0, plLength));
|
|
if (!pFX->m_prs->Check(nRetCode))
|
|
pFX->m_prs->ThrowDBException(nRetCode);
|
|
}
|
|
|
|
// Add the member address to the field map
|
|
pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::BindParam:
|
|
// CLongBinary parameters are not supported
|
|
ASSERT(FALSE);
|
|
|
|
case CFieldExchange::MarkForAddNew:
|
|
case CFieldExchange::MarkForUpdate:
|
|
// We do not archive LongBinary values
|
|
case CFieldExchange::StoreField:
|
|
case CFieldExchange::LoadField:
|
|
// We do not archive LongBinary values
|
|
#endif // _DEBUG
|
|
default:
|
|
return;
|
|
|
|
case CFieldExchange::Fixup:
|
|
// Get the size of the long binary field
|
|
*plLength = pFX->GetLongBinarySize(nField);
|
|
|
|
// Get the data if necessary
|
|
if (*plLength != SQL_NULL_DATA)
|
|
pFX->GetLongBinaryData(nField, value, plLength);
|
|
|
|
// Set the status and length
|
|
if (*plLength == SQL_NULL_DATA)
|
|
{
|
|
// Field NULL, set length and status
|
|
value.m_dwDataLength = 0;
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
}
|
|
else
|
|
{
|
|
// Field not NULL, clear the status (length already set)
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
}
|
|
|
|
return;
|
|
|
|
case CFieldExchange::NameValue:
|
|
if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
*pFX->m_pstr += szName;
|
|
*pFX->m_pstr += '=';
|
|
}
|
|
|
|
// Fall through
|
|
case CFieldExchange::Value:
|
|
if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
// If user marked column NULL, reflect this in length
|
|
if (pFX->m_prs->IsFieldStatusNull(nField - 1))
|
|
*plLength = SQL_NULL_DATA;
|
|
else
|
|
{
|
|
// Indicate data will be sent after SQLExecute
|
|
*plLength = SQL_DATA_AT_EXEC;
|
|
}
|
|
|
|
// If optimizing for bulk add, only need lengths set correctly
|
|
if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
|
|
{
|
|
*pFX->m_pstr += '?';
|
|
*pFX->m_pstr += pFX->m_lpszSeparator;
|
|
pFX->m_nParamFields++;
|
|
|
|
// Assumes all bound fields BEFORE unbound fields
|
|
CODBCFieldInfo* pODBCInfo =
|
|
&pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
|
|
|
|
AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
|
|
(UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
|
|
SQL_C_DEFAULT, pODBCInfo->m_nSQLType,
|
|
value.m_dwDataLength, 0, &value, 0, plLength));
|
|
if (nRetCode != SQL_SUCCESS)
|
|
pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
|
|
}
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::BindFieldForUpdate:
|
|
if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
|
|
{
|
|
// If user marked column NULL, reflect this in length
|
|
if (pFX->m_prs->IsFieldStatusNull(nField - 1))
|
|
*plLength = SQL_NULL_DATA;
|
|
else
|
|
{
|
|
// Length is signed value, it's limited by LONG_MAX
|
|
if (value.m_dwDataLength >
|
|
(ULONG)(LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET)))
|
|
{
|
|
ASSERT(FALSE);
|
|
*plLength = LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET);
|
|
}
|
|
else
|
|
*plLength = value.m_dwDataLength;
|
|
|
|
*plLength = SQL_LEN_DATA_AT_EXEC(*plLength);
|
|
}
|
|
}
|
|
else
|
|
*plLength = SQL_IGNORE;
|
|
|
|
return;
|
|
|
|
case CFieldExchange::UnbindFieldForUpdate:
|
|
*plLength = value.m_dwDataLength;
|
|
return;
|
|
|
|
case CFieldExchange::SetFieldNull:
|
|
if ((pFX->m_pvField == NULL &&
|
|
pFX->m_nFieldType == CFieldExchange::outputColumn) ||
|
|
pFX->m_pvField == &value)
|
|
{
|
|
if (pFX->m_bField)
|
|
{
|
|
// Mark fields null
|
|
pFX->m_prs->SetNullFieldStatus(nField - 1);
|
|
value.m_dwDataLength = 0;
|
|
*plLength = SQL_NULL_DATA;
|
|
}
|
|
else
|
|
{
|
|
pFX->m_prs->ClearNullFieldStatus(nField - 1);
|
|
|
|
if (pFX->m_prs->m_bUseUpdateSQL)
|
|
*plLength = SQL_DATA_AT_EXEC;
|
|
else
|
|
{
|
|
// Length is signed value, it's limited by LONG_MAX
|
|
if (value.m_dwDataLength >
|
|
(ULONG)(LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET)))
|
|
{
|
|
ASSERT(FALSE);
|
|
*plLength = LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET);
|
|
}
|
|
else
|
|
*plLength = value.m_dwDataLength;
|
|
|
|
*plLength = SQL_LEN_DATA_AT_EXEC(*plLength);
|
|
}
|
|
}
|
|
#ifdef _DEBUG
|
|
pFX->m_nFieldFound = nField;
|
|
#endif
|
|
}
|
|
return;
|
|
|
|
case CFieldExchange::AllocCache:
|
|
// Caching not supported for long binary
|
|
return;
|
|
|
|
#ifdef _DEBUG
|
|
case CFieldExchange::DumpField:
|
|
*pFX->m_pdcDump << "\n" << szName << " = ";
|
|
value.Dump(*pFX->m_pdcDump);
|
|
return;
|
|
#endif // _DEBUG
|
|
|
|
}
|
|
}
|
|
|
|
long CFieldExchange::GetLongBinarySize(int nField)
|
|
{
|
|
RETCODE nRetCode;
|
|
int nDummy;
|
|
long lSize;
|
|
|
|
// Give empty buffer to find size of entire LongBinary
|
|
AFX_ODBC_CALL(::SQLGetData(m_prs->m_hstmt,
|
|
(UWORD)nField, SQL_C_DEFAULT, &nDummy, 0, &lSize));
|
|
|
|
switch (nRetCode)
|
|
{
|
|
case SQL_SUCCESS:
|
|
break;
|
|
|
|
case SQL_SUCCESS_WITH_INFO:
|
|
#ifdef _DEBUG
|
|
if (afxTraceFlags & traceDatabase)
|
|
{
|
|
CDBException* e = new CDBException(nRetCode);
|
|
e->BuildErrorString(m_prs->m_pDatabase,
|
|
m_prs->m_hstmt, FALSE);
|
|
|
|
// Ignore data truncated messages
|
|
if (e->m_strStateNativeOrigin.Find(_T("State:01004")) < 0)
|
|
{
|
|
TRACE0("Warning: ODBC Success With Info, ");
|
|
e->TraceErrorMessage(e->m_strError);
|
|
e->TraceErrorMessage(e->m_strStateNativeOrigin);
|
|
}
|
|
e->Delete();
|
|
}
|
|
#endif // _DEBUG
|
|
break;
|
|
|
|
default:
|
|
m_prs->ThrowDBException(nRetCode);
|
|
}
|
|
|
|
return lSize;
|
|
}
|
|
|
|
void CFieldExchange::GetLongBinaryData(int nField, CLongBinary& lb,
|
|
long* plSize)
|
|
{
|
|
RETCODE nRetCode;
|
|
long lActualDataSize = 0;
|
|
long lChunkDataSize;
|
|
long lReallocSize;
|
|
const BYTE* lpLongBinary;
|
|
|
|
lb.m_dwDataLength = 0;
|
|
|
|
// Determine initial chunk sizes
|
|
if (*plSize == SQL_NO_TOTAL)
|
|
{
|
|
lChunkDataSize = m_lDefaultLBFetchSize;
|
|
lReallocSize = m_lDefaultLBReallocSize;
|
|
}
|
|
else
|
|
{
|
|
lChunkDataSize = *plSize;
|
|
lReallocSize = *plSize;
|
|
}
|
|
|
|
do
|
|
{
|
|
// Check if CLongBianary is big enough
|
|
lpLongBinary = ReallocLongBinary(lb,
|
|
(long)lb.m_dwDataLength + lChunkDataSize,
|
|
(long)lb.m_dwDataLength + lReallocSize);
|
|
|
|
// Adjust the pointer so that data added at end
|
|
lpLongBinary += lb.m_dwDataLength;
|
|
|
|
AFX_ODBC_CALL(::SQLGetData(m_prs->m_hstmt, (UWORD)nField,
|
|
SQL_C_BINARY, (UCHAR*)lpLongBinary, lChunkDataSize, &lActualDataSize));
|
|
::GlobalUnlock(lb.m_hData);
|
|
|
|
switch (nRetCode)
|
|
{
|
|
case SQL_NO_DATA_FOUND:
|
|
m_prs->SetNullFieldStatus(nField - 1);
|
|
*plSize = SQL_NULL_DATA;
|
|
break;
|
|
|
|
case SQL_SUCCESS:
|
|
// All data fetched
|
|
lb.m_dwDataLength += lActualDataSize;
|
|
*plSize = (long)lb.m_dwDataLength;
|
|
return;
|
|
|
|
case SQL_SUCCESS_WITH_INFO:
|
|
{
|
|
CDBException* e = new CDBException(nRetCode);
|
|
e->BuildErrorString(m_prs->m_pDatabase, m_prs->m_hstmt,
|
|
FALSE);
|
|
|
|
// Ignore data truncated messages
|
|
if (e->m_strStateNativeOrigin.Find(_T("State:01004")) < 0)
|
|
{
|
|
#ifdef _DEBUG
|
|
if (afxTraceFlags & traceDatabase)
|
|
{
|
|
TRACE0("Warning: ODBC Success With Info, ");
|
|
e->TraceErrorMessage(e->m_strError);
|
|
e->TraceErrorMessage(e->m_strStateNativeOrigin);
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// Must be some other warning, should be finished
|
|
lb.m_dwDataLength += lActualDataSize;
|
|
}
|
|
else
|
|
{
|
|
// Should only happen if SQL_NO_TOTAL
|
|
|
|
// Increment the length by the chunk size for subsequent SQLGetData call
|
|
lb.m_dwDataLength += lChunkDataSize;
|
|
|
|
// Recalculate chunk and alloc sizes
|
|
lChunkDataSize = m_prs->GetLBFetchSize(lChunkDataSize);
|
|
lReallocSize = m_prs->GetLBReallocSize(lReallocSize);
|
|
}
|
|
|
|
*plSize = (long)lb.m_dwDataLength;
|
|
e->Delete();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
m_prs->ThrowDBException(nRetCode);
|
|
}
|
|
|
|
} while (lActualDataSize == SQL_NO_TOTAL);
|
|
}
|
|
|
|
BYTE* CFieldExchange::ReallocLongBinary(CLongBinary& lb, long lSizeRequired,
|
|
long lReallocSize)
|
|
{
|
|
// realloc max of lSizeRequired, lReallocSize (m_dwDataLength untouched)
|
|
|
|
if (lSizeRequired < 0)
|
|
{
|
|
ASSERT(FALSE);
|
|
lSizeRequired = 0;
|
|
}
|
|
|
|
HGLOBAL hOldData = NULL;
|
|
|
|
// Allocate or Realloc as req'd
|
|
if (lb.m_hData == NULL)
|
|
lb.m_hData = ::GlobalAlloc(GMEM_MOVEABLE, lReallocSize);
|
|
else
|
|
{
|
|
DWORD dwSize = ::GlobalSize(lb.m_hData);
|
|
if (dwSize < (DWORD)lSizeRequired)
|
|
{
|
|
// Save the old handle in case ReAlloc fails
|
|
hOldData = lb.m_hData;
|
|
|
|
// Allocate more memory if necessary
|
|
lb.m_hData = ::GlobalReAlloc(lb.m_hData,
|
|
__max(lSizeRequired, lReallocSize), GMEM_MOVEABLE);
|
|
}
|
|
}
|
|
|
|
// Validate the memory was allocated and can be locked
|
|
if (lb.m_hData == NULL)
|
|
{
|
|
// Restore the old handle (not NULL if Realloc failed)
|
|
lb.m_hData = hOldData;
|
|
AfxThrowMemoryException();
|
|
}
|
|
|
|
BYTE* lpLongBinary = (BYTE*)::GlobalLock(lb.m_hData);
|
|
if (lpLongBinary == NULL)
|
|
{
|
|
::GlobalFree(lb.m_hData);
|
|
lb.m_hData = NULL;
|
|
AfxThrowMemoryException();
|
|
}
|
|
|
|
return lpLongBinary;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Recordset Field Exchange Helpers
|
|
|
|
|
|
void AFXAPI AfxStoreField(CRecordset& rs, UINT nField, void* pvField)
|
|
{
|
|
// Get the field data
|
|
CFieldInfo* pInfo = &rs.m_rgFieldInfos[nField - 1];
|
|
|
|
// Cache the status
|
|
pInfo->m_bStatus = rs.GetFieldStatus(nField - 1);
|
|
|
|
// Save the data
|
|
if (!rs.IsFieldStatusNull(nField - 1))
|
|
{
|
|
// Don't need to save length for variable len
|
|
// objects as CString and CByteArray track len internally
|
|
long nDummyLength;
|
|
|
|
if (pInfo->m_nDataType == AFX_RFX_BOOL ||
|
|
pInfo->m_nDataType == AFX_RFX_BYTE ||
|
|
pInfo->m_nDataType == AFX_RFX_INT ||
|
|
pInfo->m_nDataType == AFX_RFX_LONG ||
|
|
pInfo->m_nDataType == AFX_RFX_SINGLE)
|
|
{
|
|
// If caching data by value, pass a ref
|
|
AfxCopyValueByRef(pvField, &pInfo->m_pvDataCache,
|
|
&nDummyLength, pInfo->m_nDataType);
|
|
}
|
|
else
|
|
{
|
|
AfxCopyValueByRef(pvField, pInfo->m_pvDataCache,
|
|
&nDummyLength, pInfo->m_nDataType);
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
// Cache the bind address expected by ODBC
|
|
switch(pInfo->m_nDataType)
|
|
{
|
|
default:
|
|
// All types that are bound directly
|
|
pInfo->m_pvBindAddress = pvField;
|
|
break;
|
|
|
|
case AFX_RFX_TEXT:
|
|
#ifdef _UNICODE
|
|
pInfo->m_pvBindAddress = rs.m_pvFieldProxy[nField-1];
|
|
#else
|
|
pInfo->m_pvBindAddress = ((CString*)pvField)->GetBuffer(0);
|
|
((CString*)pvField)->ReleaseBuffer();
|
|
#endif
|
|
break;
|
|
|
|
case AFX_RFX_DATE:
|
|
pInfo->m_pvBindAddress = rs.m_pvFieldProxy[nField-1];
|
|
break;
|
|
|
|
case AFX_RFX_BINARY:
|
|
pInfo->m_pvBindAddress = ((CByteArray*)pvField)->GetData();
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void AFXAPI AfxLoadField(CRecordset& rs, UINT nField,
|
|
void* pvField, long* plLength)
|
|
{
|
|
// Get the field data
|
|
CFieldInfo* pInfo = &rs.m_rgFieldInfos[nField - 1];
|
|
|
|
// Assumes old field status cleared out
|
|
rs.SetFieldStatus(nField - 1, pInfo->m_bStatus);
|
|
|
|
// If not NULL, restore the value and the length
|
|
if (!rs.IsFieldStatusNull(nField - 1))
|
|
{
|
|
if (pInfo->m_nDataType == AFX_RFX_BOOL ||
|
|
pInfo->m_nDataType == AFX_RFX_BYTE ||
|
|
pInfo->m_nDataType == AFX_RFX_INT ||
|
|
pInfo->m_nDataType == AFX_RFX_LONG ||
|
|
pInfo->m_nDataType == AFX_RFX_SINGLE)
|
|
{
|
|
// If caching data by value, pass a ref
|
|
AfxCopyValueByRef(&pInfo->m_pvDataCache, pvField,
|
|
plLength, pInfo->m_nDataType);
|
|
}
|
|
else
|
|
{
|
|
AfxCopyValueByRef(pInfo->m_pvDataCache, pvField,
|
|
plLength, pInfo->m_nDataType);
|
|
}
|
|
}
|
|
else
|
|
*plLength = SQL_NULL_DATA;
|
|
|
|
#ifdef _DEBUG
|
|
// Buffer address must not change - ODBC's SQLBindCol depends upon this
|
|
if (pInfo->m_nDataType == AFX_RFX_BINARY)
|
|
{
|
|
// Change pvField to point to the data of the CByteArray
|
|
pvField = ((CByteArray*)pvField)->GetData();
|
|
}
|
|
|
|
if (pInfo->m_pvBindAddress != pvField)
|
|
{
|
|
TRACE1("Error: field address (column %u) has changed!\n",
|
|
nField);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
BOOL AFXAPI AfxCompareValueByRef(void* pvSrc, void* pvDest, int nSrcType)
|
|
{
|
|
ASSERT(pvSrc != NULL);
|
|
ASSERT(pvDest != NULL);
|
|
|
|
BOOL bCompare = FALSE;
|
|
|
|
switch(nSrcType)
|
|
{
|
|
|
|
case AFX_RFX_LONGBINARY:
|
|
// Caching long binary Src not supported
|
|
default:
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
case AFX_RFX_TEXT:
|
|
if (*(CString*)pvDest == *(CString*)pvSrc)
|
|
bCompare = TRUE;
|
|
break;
|
|
|
|
case AFX_RFX_BINARY:
|
|
{
|
|
CByteArray* pByteArraySrc = (CByteArray*)pvSrc;
|
|
CByteArray* pByteArrayDest = (CByteArray*)pvDest;
|
|
|
|
// If sizes compare, compare the Src
|
|
int nSize = pByteArraySrc->GetSize();
|
|
if (nSize == pByteArrayDest->GetSize())
|
|
{
|
|
if (memcmp(&pByteArrayDest[0], &pByteArraySrc[0], nSize) == 0)
|
|
bCompare = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case AFX_RFX_BOOL:
|
|
if (*(BOOL*)pvDest == *(BOOL*)pvSrc)
|
|
bCompare = TRUE;
|
|
break;
|
|
|
|
case AFX_RFX_BYTE:
|
|
if (*(BYTE*)pvDest == *(BYTE*)pvSrc)
|
|
bCompare = TRUE;
|
|
break;
|
|
|
|
case AFX_RFX_INT:
|
|
if (*(int*)pvDest == *(int*)pvSrc)
|
|
bCompare = TRUE;
|
|
break;
|
|
|
|
case AFX_RFX_LONG:
|
|
if (*(long*)pvDest == *(long*)pvSrc)
|
|
bCompare = TRUE;
|
|
break;
|
|
|
|
case AFX_RFX_SINGLE:
|
|
if (*(float*)pvDest == *(float*)pvSrc)
|
|
bCompare = TRUE;
|
|
break;
|
|
|
|
case AFX_RFX_DOUBLE:
|
|
if (*(double*)pvDest == *(double*)pvSrc)
|
|
bCompare = TRUE;
|
|
break;
|
|
|
|
case AFX_RFX_DATE:
|
|
if (*(CTime*)pvDest == *(CTime*)pvSrc)
|
|
bCompare = TRUE;
|
|
break;
|
|
|
|
case AFX_RFX_TIMESTAMP:
|
|
{
|
|
TIMESTAMP_STRUCT* pSrc = (TIMESTAMP_STRUCT*)pvSrc;
|
|
TIMESTAMP_STRUCT* pDest = (TIMESTAMP_STRUCT*)pvDest;
|
|
if ((pSrc->year == pDest->year &&
|
|
pSrc->month == pDest->month &&
|
|
pSrc->day == pDest->day &&
|
|
pSrc->hour == pDest->hour &&
|
|
pSrc->minute == pDest->minute &&
|
|
pSrc->second == pDest->second &&
|
|
pSrc->fraction == pDest->fraction))
|
|
{
|
|
bCompare = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return bCompare;
|
|
}
|
|
|
|
void AFXAPI AfxCopyValueByRef(void* pvSrc, void* pvDest, long* plLength, int nDestType)
|
|
{
|
|
ASSERT(pvSrc != NULL);
|
|
ASSERT(pvDest != NULL);
|
|
ASSERT(plLength != NULL);
|
|
|
|
switch (nDestType)
|
|
{
|
|
case AFX_RFX_LONGBINARY:
|
|
// Caching long binary Dest not supported
|
|
default:
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
case AFX_RFX_TEXT:
|
|
*(CString*)pvDest = *(CString*)pvSrc;
|
|
*plLength = ((CString*)pvSrc)->GetLength();
|
|
break;
|
|
|
|
case AFX_RFX_BINARY:
|
|
((CByteArray*)pvDest)->Copy(*(CByteArray*)pvSrc);
|
|
*plLength = ((CByteArray*)pvSrc)->GetSize();
|
|
break;
|
|
|
|
case AFX_RFX_BOOL:
|
|
*(BOOL*)pvDest = *(BOOL*)pvSrc;
|
|
*plLength = sizeof(BOOL);
|
|
break;
|
|
|
|
case AFX_RFX_BYTE:
|
|
*(BYTE*)pvDest = *(BYTE*)pvSrc;
|
|
*plLength = sizeof(BYTE);
|
|
break;
|
|
|
|
case AFX_RFX_INT:
|
|
*(int*)pvDest = *(int*)pvSrc;
|
|
*plLength = sizeof(int);
|
|
break;
|
|
|
|
case AFX_RFX_LONG:
|
|
*(long*)pvDest = *(long*)pvSrc;
|
|
*plLength = sizeof(long);
|
|
break;
|
|
|
|
case AFX_RFX_SINGLE:
|
|
*(float*)pvDest = *(float*)pvSrc;
|
|
*plLength = sizeof(float);
|
|
break;
|
|
|
|
case AFX_RFX_DOUBLE:
|
|
*(double*)pvDest = *(double*)pvSrc;
|
|
*plLength = sizeof(double);
|
|
break;
|
|
|
|
case AFX_RFX_DATE:
|
|
*(CTime*)pvDest = *(CTime*)pvSrc;
|
|
*plLength = sizeof(TIMESTAMP_STRUCT);
|
|
break;
|
|
|
|
case AFX_RFX_TIMESTAMP:
|
|
{
|
|
TIMESTAMP_STRUCT* pDest = (TIMESTAMP_STRUCT*)pvDest;
|
|
TIMESTAMP_STRUCT* pSrc = (TIMESTAMP_STRUCT*)pvSrc;
|
|
|
|
pDest->year = pSrc->year;
|
|
pDest->month = pSrc->month;
|
|
pDest->day = pSrc->day;
|
|
pDest->hour = pSrc->hour;
|
|
pDest->minute = pSrc->minute;
|
|
pDest->second = pSrc->second;
|
|
pDest->fraction = pSrc->fraction;
|
|
|
|
*plLength = sizeof(TIMESTAMP_STRUCT);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Bulk Recordset Field Exchange
|
|
|
|
void AFXAPI RFX_Text_Bulk(CFieldExchange* pFX, LPCTSTR szName,
|
|
LPSTR* prgStrVals, long** prgLengths, int nMaxLength)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::AllocMultiRowBuffer:
|
|
{
|
|
// The buffer pointer better be initialized to NULL
|
|
// or cleanup in exceptional cases mail fail
|
|
ASSERT(*prgStrVals == NULL);
|
|
ASSERT(*prgLengths == NULL);
|
|
|
|
int nRowsetSize = pFX->m_prs->GetRowsetSize();
|
|
|
|
// Allocate buffers to hold data and length
|
|
*prgStrVals = new char[nRowsetSize * nMaxLength];
|
|
*prgLengths = new long[nRowsetSize];
|
|
}
|
|
break;
|
|
|
|
case CFieldExchange::DeleteMultiRowBuffer:
|
|
delete [] *prgStrVals;
|
|
*prgStrVals = NULL;
|
|
|
|
delete [] *prgLengths;
|
|
*prgLengths = NULL;
|
|
break;
|
|
|
|
default:
|
|
AfxRFXBulkDefault(pFX, szName, *prgStrVals, *prgLengths,
|
|
SQL_C_CHAR, nMaxLength);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Bool_Bulk(CFieldExchange* pFX, LPCTSTR szName,
|
|
BOOL** prgBoolVals, long** prgLengths)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::AllocMultiRowBuffer:
|
|
{
|
|
// The buffer pointer better be initialized to NULL
|
|
// or cleanup in exceptional cases mail fail
|
|
ASSERT(*prgBoolVals == NULL);
|
|
ASSERT(*prgLengths == NULL);
|
|
|
|
int nRowsetSize = pFX->m_prs->GetRowsetSize();
|
|
|
|
// Allocate buffers to hold data and length
|
|
*prgBoolVals = new BOOL[nRowsetSize];
|
|
*prgLengths = new long[nRowsetSize];
|
|
}
|
|
break;
|
|
|
|
case CFieldExchange::DeleteMultiRowBuffer:
|
|
delete [] *prgBoolVals;
|
|
*prgBoolVals = NULL;
|
|
|
|
delete [] *prgLengths;
|
|
*prgLengths = NULL;
|
|
break;
|
|
|
|
default:
|
|
AfxRFXBulkDefault(pFX, szName, *prgBoolVals, *prgLengths,
|
|
SQL_C_LONG, sizeof(BOOL));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Int_Bulk(CFieldExchange* pFX, LPCTSTR szName,
|
|
int** prgIntVals, long** prgLengths)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::AllocMultiRowBuffer:
|
|
{
|
|
// The buffer pointer better be initialized to NULL
|
|
// or cleanup in exceptional cases mail fail
|
|
ASSERT(*prgIntVals == NULL);
|
|
ASSERT(*prgLengths == NULL);
|
|
|
|
int nRowsetSize = pFX->m_prs->GetRowsetSize();
|
|
|
|
// Allocate buffers to hold data and length
|
|
*prgIntVals = new int[nRowsetSize];
|
|
*prgLengths = new long[nRowsetSize];
|
|
}
|
|
break;
|
|
|
|
case CFieldExchange::DeleteMultiRowBuffer:
|
|
delete [] *prgIntVals;
|
|
*prgIntVals = NULL;
|
|
|
|
delete [] *prgLengths;
|
|
*prgLengths = NULL;
|
|
break;
|
|
|
|
default:
|
|
AfxRFXBulkDefault(pFX, szName, *prgIntVals, *prgLengths,
|
|
SQL_C_LONG, sizeof(int));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Long_Bulk(CFieldExchange* pFX, LPCTSTR szName,
|
|
long** prgLongVals, long** prgLengths)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::AllocMultiRowBuffer:
|
|
{
|
|
// The buffer pointer better be initialized to NULL
|
|
// or cleanup in exceptional cases mail fail
|
|
ASSERT(*prgLongVals == NULL);
|
|
ASSERT(*prgLengths == NULL);
|
|
|
|
int nRowsetSize = pFX->m_prs->GetRowsetSize();
|
|
|
|
// Allocate buffers to hold data and length
|
|
*prgLongVals = new long[nRowsetSize];
|
|
*prgLengths = new long[nRowsetSize];
|
|
}
|
|
break;
|
|
|
|
case CFieldExchange::DeleteMultiRowBuffer:
|
|
delete [] *prgLongVals;
|
|
*prgLongVals = NULL;
|
|
|
|
delete [] *prgLengths;
|
|
*prgLengths = NULL;
|
|
break;
|
|
|
|
default:
|
|
AfxRFXBulkDefault(pFX, szName, *prgLongVals, *prgLengths,
|
|
SQL_C_LONG, sizeof(long));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Date_Bulk(CFieldExchange* pFX, LPCTSTR szName,
|
|
TIMESTAMP_STRUCT** prgTSVals, long** prgLengths)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::AllocMultiRowBuffer:
|
|
{
|
|
// The buffer pointer better be initialized to NULL
|
|
// or cleanup in exceptional cases mail fail
|
|
ASSERT(*prgTSVals == NULL);
|
|
ASSERT(*prgLengths == NULL);
|
|
|
|
int nRowsetSize = pFX->m_prs->GetRowsetSize();
|
|
|
|
// Allocate buffers to hold data and length
|
|
*prgTSVals = new TIMESTAMP_STRUCT[nRowsetSize];
|
|
*prgLengths = new long[nRowsetSize];
|
|
}
|
|
break;
|
|
|
|
case CFieldExchange::DeleteMultiRowBuffer:
|
|
delete [] *prgTSVals;
|
|
*prgTSVals = NULL;
|
|
|
|
delete [] *prgLengths;
|
|
*prgLengths = NULL;
|
|
break;
|
|
|
|
default:
|
|
AfxRFXBulkDefault(pFX, szName, *prgTSVals, *prgLengths,
|
|
SQL_C_TIMESTAMP, sizeof(TIMESTAMP_STRUCT));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Byte_Bulk(CFieldExchange* pFX, LPCTSTR szName,
|
|
BYTE** prgByteVals, long** prgLengths)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::AllocMultiRowBuffer:
|
|
{
|
|
// The buffer pointer better be initialized to NULL
|
|
// or cleanup in exceptional cases mail fail
|
|
ASSERT(*prgByteVals == NULL);
|
|
ASSERT(*prgLengths == NULL);
|
|
|
|
int nRowsetSize = pFX->m_prs->GetRowsetSize();
|
|
|
|
// Allocate buffers to hold data and length
|
|
*prgByteVals = new BYTE[nRowsetSize];
|
|
*prgLengths = new long[nRowsetSize];
|
|
}
|
|
break;
|
|
|
|
case CFieldExchange::DeleteMultiRowBuffer:
|
|
delete [] *prgByteVals;
|
|
*prgByteVals = NULL;
|
|
|
|
delete [] *prgLengths;
|
|
*prgLengths = NULL;
|
|
break;
|
|
|
|
default:
|
|
AfxRFXBulkDefault(pFX, szName, *prgByteVals, *prgLengths,
|
|
SQL_C_TINYINT, sizeof(BYTE));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AFXAPI RFX_Binary_Bulk(CFieldExchange* pFX, LPCTSTR szName,
|
|
BYTE** prgByteVals, long** prgLengths, int nMaxLength)
|
|
{
|
|
ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
|
|
ASSERT(AfxIsValidString(szName));
|
|
|
|
UINT nField;
|
|
if (!pFX->IsFieldType(&nField))
|
|
return;
|
|
|
|
switch (pFX->m_nOperation)
|
|
{
|
|
case CFieldExchange::AllocMultiRowBuffer:
|
|
{
|
|
// The buffer pointer better be initialized to NULL
|
|
// or cleanup in exceptional cases mail fail
|
|
ASSERT(*prgByteVals == NULL);
|
|
ASSERT(*prgLengths == NULL);
|
|
|
|
int nRowsetSize = pFX->m_prs->GetRowsetSize();
|
|
|
|
// Allocate buffers to hold data and length
|
|
*prgByteVals = new BYTE[nRowsetSize * nMaxLength];
|
|
*prgLengths = new long[nRowsetSize];
|
|
}
|
|
break;
|
|
|
|
case CFieldExchange::DeleteMultiRowBuffer:
|
|
delete [] *prgByteVals;
|
|
*prgByteVals = NULL;
|
|
|
|
delete [] *prgLengths;
|
|
*prgLengths = NULL;
|
|
break;
|
|
|
|
default:
|
|
AfxRFXBulkDefault(pFX, szName, *prgByteVals, *prgLengths,
|
|
SQL_C_BINARY, nMaxLength);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AFXAPI AfxRFXBulkDefault(CFieldExchange* pFX, LPCTSTR szName,
|
|
void* pv, long* rgLengths, int nCType, UINT cbValue)
|
|
{
|
|
RETCODE nRetCode;
|
|
|
|
switch(pFX->m_nOperation)
|
|
{
|
|
default:
|
|
// Operation not valid for bulk fetch
|
|
ASSERT(FALSE);
|
|
return;
|
|
|
|
case CFieldExchange::Name:
|
|
// We require a name
|
|
ASSERT(lstrlen(szName) != 0);
|
|
|
|
*pFX->m_pstr += szName;
|
|
*pFX->m_pstr += pFX->m_lpszSeparator;
|
|
break;
|
|
|
|
case CFieldExchange::BindFieldToColumn:
|
|
AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt,
|
|
(UWORD)pFX->m_nFields, nCType, pv, cbValue, rgLengths));
|
|
if (!pFX->m_prs->Check(nRetCode))
|
|
pFX->m_prs->ThrowDBException(nRetCode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Inline function declarations expanded out-of-line
|
|
|
|
#ifndef _AFX_ENABLE_INLINES
|
|
|
|
static char _szAfxDbInl[] = "afxdb.inl";
|
|
#undef THIS_FILE
|
|
#define THIS_FILE _szAfxDbInl
|
|
#define _AFXDBRFX_INLINE
|
|
#include "afxdb.inl"
|
|
|
|
#endif
|
|
|
|
#ifdef AFX_INIT_SEG
|
|
#pragma code_seg(AFX_INIT_SEG)
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|