home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / SRC / DBRFX.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-05  |  69.3 KB  |  2,724 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1997 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_DB_SEG
  14. #pragma code_seg(AFX_DB_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // CDBByteArray db specific class for holding byte array data
  26. class CDBByteArray : public CByteArray
  27. {
  28.     DECLARE_DYNAMIC(CDBByteArray)
  29.  
  30. // Operations
  31.     void SetLength(int nNewSize);
  32. };
  33.  
  34. inline void CDBByteArray::SetLength(int nNewSize)
  35. {
  36.     // Can't grow buffer since ODBC has been SQLBindCol'd on it.
  37.     ASSERT(nNewSize <= m_nMaxSize);
  38.     m_nSize = nNewSize;
  39. }
  40.  
  41. //////////////////////////////////////////////////////////////////////////////
  42. // CFieldExchange
  43.  
  44. CFieldExchange::CFieldExchange(UINT nOperation, CRecordset* prs, void* pvField)
  45. {
  46.     ASSERT(nOperation >= BindParam && nOperation <= DumpField);
  47.     ASSERT_VALID(prs);
  48.     ASSERT(prs->m_hstmt != SQL_NULL_HSTMT);
  49.  
  50.     m_nFieldType = (UINT) noFieldType;
  51.     m_nOperation = nOperation;
  52.     m_prs = prs;
  53.     m_pvField = pvField;
  54.  
  55.     m_nFields = 0;
  56.     m_nParams = 0;
  57.     m_nParamFields = 0;
  58.     m_bField = FALSE;
  59.     m_pstr = NULL;
  60.     m_hstmt = SQL_NULL_HSTMT;
  61.  
  62.     m_lDefaultLBFetchSize = 0x00010000;
  63.     m_lDefaultLBReallocSize = 0x00010000;
  64. }
  65.  
  66. BOOL CFieldExchange::IsFieldType(UINT* pnField)
  67. {
  68.     if (m_nFieldType == outputColumn)
  69.     {
  70.         *pnField = ++m_nFields;
  71.         // Recordset's m_nFields must match number of Fields!
  72.         ASSERT(m_nFields <= m_prs->m_nFields);
  73.     }
  74.     else
  75.     {
  76.         // Make sure SetFieldType was called
  77.         ASSERT(m_nFieldType == inputParam ||
  78.             m_nFieldType == outputParam ||
  79.             m_nFieldType == inoutParam);
  80.  
  81.         *pnField = ++m_nParams;
  82.         // Recordset's m_nParams must match number of Params!
  83.         ASSERT(m_nParams <= m_prs->m_nParams);
  84.     }
  85.  
  86.     if (m_nOperation == BindParam || m_nOperation == RebindParam)
  87.     {
  88.         // only valid on a param field type
  89.         return m_nFieldType != outputColumn;
  90.     }
  91.     else
  92.     {
  93.         // valid only on an outputColumn field type
  94.         return m_nFieldType == outputColumn;
  95.     }
  96. }
  97.  
  98. // Default implementation for RFX functions
  99. void CFieldExchange::Default(LPCTSTR szName,
  100.     void* pv, LONG* plLength, int nCType, UINT cbValue, UINT cbPrecision)
  101. {
  102.     RETCODE nRetCode;
  103.     UINT nField = (m_nFieldType == outputColumn)? m_nFields: m_nParams;
  104.     switch (m_nOperation)
  105.     {
  106.     case BindParam:
  107.         if (m_prs->IsParamStatusNull(nField - 1))
  108.             *plLength = SQL_NULL_DATA;
  109.         else
  110.             *plLength = cbValue;
  111.         // For params, CType is same as SQL type
  112.         AFX_SQL_SYNC(::SQLBindParameter(m_hstmt, (UWORD)nField,
  113.             (SWORD)m_nFieldType, (SWORD)nCType, (SWORD)nCType, cbPrecision, 0,
  114.             pv, 0, plLength));
  115.         if (nRetCode != SQL_SUCCESS)
  116.             m_prs->ThrowDBException(nRetCode, m_hstmt);
  117.  
  118.         // Add the member address to the param map
  119.         m_prs->m_mapParamIndex.SetAt(pv, (void*)nField);
  120.         return;
  121.  
  122.     case RebindParam:
  123.         // Only need to reset param length
  124.         *plLength = m_prs->IsParamStatusNull(nField - 1) ? SQL_NULL_DATA : cbValue;
  125.         return;
  126.  
  127.     case BindFieldForUpdate:
  128.         if (!m_prs->IsFieldStatusDirty(nField - 1))
  129.         {
  130.             // If not dirty, set length to SQL_IGNORE for SQLSetPos updates
  131.             *plLength = SQL_IGNORE;
  132.         }
  133.         else if (!m_prs->IsFieldStatusNull(nField - 1))
  134.         {
  135.             // Reset the length as it may have changed for var length fields
  136.             *plLength = cbValue;
  137.         }
  138.         return;
  139.  
  140.  
  141.     case UnbindFieldForUpdate:
  142.         // Reset bound length to actual length to clear SQL_IGNOREs
  143.         if (!m_prs->IsFieldStatusDirty(nField - 1))
  144.             *plLength = cbValue;
  145.         return;
  146.  
  147.     case BindFieldToColumn:
  148.         AFX_SQL_SYNC(::SQLBindCol(m_prs->m_hstmt, (UWORD)nField, (SWORD)nCType,
  149.             pv, cbValue, plLength));
  150.         if (!m_prs->Check(nRetCode))
  151.             m_prs->ThrowDBException(nRetCode);
  152.  
  153.         // Add the member address to the field map
  154.         m_prs->m_mapFieldIndex.SetAt(pv, (void*)nField);
  155.         return;
  156.  
  157.     case Name:
  158.         if (m_prs->IsFieldStatusDirty(nField - 1))
  159.         {
  160.             // We require a name
  161.             ASSERT(lstrlen(szName) != 0);
  162.  
  163.             *m_pstr += szName;
  164.             *m_pstr += m_lpszSeparator;
  165.         }
  166.         return;
  167.  
  168.     case NameValue:
  169.         if (m_prs->IsFieldStatusDirty(nField - 1))
  170.         {
  171.             *m_pstr += szName;
  172.             *m_pstr += '=';
  173.         }
  174.  
  175.         // Fall through
  176.     case Value:
  177.         if (m_prs->IsFieldStatusDirty(nField - 1))
  178.         {
  179.             // If user marked column NULL, reflect this in length
  180.             if (m_prs->IsFieldStatusNull(nField - 1))
  181.                 *plLength = SQL_NULL_DATA;
  182.             else
  183.                 *plLength = cbValue;
  184.  
  185.             // If optimizing for bulk add, only need lengths set correctly
  186.             if(!(m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  187.             {
  188.                 *m_pstr += '?';
  189.                 *m_pstr += m_lpszSeparator;
  190.                 m_nParamFields++;
  191.  
  192.                 // Assumes all bound fields BEFORE unbound fields
  193.                 CODBCFieldInfo* pODBCInfo =
  194.                     &m_prs->m_rgODBCFieldInfos[nField - 1];
  195.  
  196.                 AFX_SQL_SYNC(::SQLBindParameter(m_hstmt,
  197.                     (UWORD)m_nParamFields, SQL_PARAM_INPUT,
  198.                     (SWORD)nCType, pODBCInfo->m_nSQLType,
  199.                     pODBCInfo->m_nPrecision, pODBCInfo->m_nScale,
  200.                     pv, 0, plLength));
  201.                 if (nRetCode != SQL_SUCCESS)
  202.                     m_prs->ThrowDBException(nRetCode, m_hstmt);
  203.             }
  204.         }
  205.         return;
  206.  
  207.     case MarkForUpdate:
  208.         {
  209.             // Get the field data
  210.             CFieldInfo* pInfo = &m_prs->m_rgFieldInfos[nField - 1];
  211.  
  212.             // If user changed field value from previous value, mark field dirty
  213.             if ((pInfo->m_bStatus & AFX_SQL_FIELD_FLAG_NULL))
  214.             {
  215.                 if (!m_prs->IsFieldStatusNull(nField - 1))
  216.                     m_prs->SetDirtyFieldStatus(nField - 1);
  217.             }
  218.             else
  219.             {
  220.                 // Saved field is not NULL. current field null, so field dirty
  221.                 BOOL bDirty = m_prs->IsFieldStatusNull(nField - 1);
  222.  
  223.                 // If values differ, then field dirty
  224.                 void* pvDataCache;
  225.  
  226.                 if (pInfo->m_nDataType == AFX_RFX_BOOL ||
  227.                     pInfo->m_nDataType == AFX_RFX_BYTE ||
  228.                     pInfo->m_nDataType == AFX_RFX_INT ||
  229.                     pInfo->m_nDataType == AFX_RFX_LONG ||
  230.                     pInfo->m_nDataType == AFX_RFX_SINGLE)
  231.                 {
  232.                     // If caching data by value, pass a ref
  233.                     pvDataCache = &pInfo->m_pvDataCache;
  234.                 }
  235.                 else
  236.                     pvDataCache = pInfo->m_pvDataCache;
  237.  
  238.                 if (bDirty || !AfxCompareValueByRef(pv, pvDataCache, pInfo->m_nDataType))
  239.                     m_prs->SetDirtyFieldStatus(nField - 1);
  240.             }
  241.  
  242. #ifdef _DEBUG
  243.             // Field address must not change - ODBC's SQLBindCol depends upon this
  244.             void* pvBind;
  245.  
  246.             switch (pInfo->m_nDataType)
  247.             {
  248.             default:
  249.                 pvBind = pv;
  250.                 break;
  251.  
  252.             case AFX_RFX_TEXT:
  253.     #ifdef _UNICODE
  254.                 pvBind = m_prs->m_pvFieldProxy[nField-1];
  255.     #else // !_UNICODE
  256.                 {
  257.                     pvBind = ((CString*)pv)->GetBuffer(0);
  258.                     ((CString*)pv)->ReleaseBuffer();
  259.                 }
  260.     #endif
  261.                 break;
  262.  
  263.             case AFX_RFX_DATE:
  264.                 pvBind = m_prs->m_pvFieldProxy[nField-1];
  265.                 break;
  266.  
  267.             case AFX_RFX_BINARY:
  268.                 pvBind = ((CByteArray*)pv)->GetData();
  269.                 break;
  270.             }
  271.  
  272.             if (pInfo->m_pvBindAddress != pvBind)
  273.             {
  274.                 TRACE1("Error: field address (column %u) has changed!\n",
  275.                     nField);
  276.                 ASSERT(FALSE);
  277.             }
  278. #endif // _DEBUG
  279.  
  280.             if ((m_pvField == NULL  || m_pvField == pv) &&
  281.                 m_prs->IsFieldStatusDirty(nField - 1))
  282.             {
  283.                 m_bField = TRUE;
  284.             }
  285.         }
  286.         return;
  287.  
  288.     case StoreField:
  289.         AfxStoreField(*m_prs, nField, pv);
  290.         return;
  291.  
  292.     case LoadField:
  293.         AfxLoadField(*m_prs, nField, pv, plLength);
  294.         return;
  295.  
  296.     default:
  297.         ASSERT(FALSE);
  298.     }
  299. }
  300.  
  301. // Note: CString.m_pchData must not be changed.  This address is registered
  302. // with ODBC and must remain valid until the recordset is released.
  303. void AFXAPI RFX_Text(CFieldExchange* pFX, LPCTSTR szName,
  304.     CString& value, int nMaxLength, int nColumnType, short nScale)
  305. {
  306.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  307.     ASSERT(AfxIsValidString(szName));
  308.     ASSERT(AfxIsValidAddress(&value, sizeof(CString)));
  309.  
  310.     RETCODE nRetCode;
  311.     UINT nField;
  312.     if (!pFX->IsFieldType(&nField))
  313.         return;
  314.  
  315.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  316.         nField - 1, pFX->m_nFieldType);
  317.     switch (pFX->m_nOperation)
  318.     {
  319.     default:
  320.         pFX->Default(szName, value.GetBuffer(0), plLength,
  321.             SQL_C_CHAR, value.GetLength(), nMaxLength);
  322.         value.ReleaseBuffer();
  323.         return;
  324.  
  325.     case CFieldExchange::BindParam:
  326.         {
  327.             // Preallocate to nMaxLength and setup binding address
  328.             value.GetBufferSetLength(nMaxLength);
  329.             void* pvParam = value.LockBuffer(); // will be overwritten if UNICODE
  330.  
  331. #ifdef _UNICODE
  332.             // Must use proxy to translate unicode data into non-unicode param
  333.             pFX->m_prs->m_bRebindParams = TRUE;
  334.  
  335.             // Allocate proxy array if necessary
  336.             if (pFX->m_prs->m_pvParamProxy == NULL)
  337.             {
  338.                 pFX->m_prs->m_pvParamProxy = new void*[pFX->m_prs->m_nParams];
  339.                 memset(pFX->m_prs->m_pvParamProxy, 0,
  340.                     pFX->m_prs->m_nParams*sizeof(void*));
  341.                 pFX->m_prs->m_nProxyParams = pFX->m_prs->m_nParams;
  342.             }
  343.  
  344.             // Allocate non-unicode string to nMaxLength if necessary for SQLBindParameter
  345.             if (pFX->m_prs->m_pvParamProxy[nField-1] == NULL)
  346.             {
  347.                 pvParam = new CHAR[nMaxLength+1];
  348.                 pFX->m_prs->m_pvParamProxy[nField-1] = pvParam;
  349.             }
  350.             else
  351.                 pvParam = pFX->m_prs->m_pvParamProxy[nField-1];
  352.  
  353.             // Now fill in the data value
  354.             USES_CONVERSION;
  355.             lstrcpyA((char*)pvParam, T2A((LPCTSTR)value));
  356. #endif // _UNICODE
  357.  
  358.             *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  359.                 SQL_NULL_DATA : SQL_NTS;
  360.  
  361.             AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt, (UWORD)nField,
  362.                  (SWORD)pFX->m_nFieldType, SQL_C_CHAR, (SWORD)nColumnType,
  363.                  nMaxLength, nScale, pvParam, 0, plLength));
  364.  
  365.             value.ReleaseBuffer();
  366.  
  367.             if (nRetCode != SQL_SUCCESS)
  368.                 pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
  369.  
  370.             // Add the member address to the param map
  371.             pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
  372.         }
  373.         return;
  374.  
  375. #ifdef _UNICODE
  376.     case CFieldExchange::RebindParam:
  377.         *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  378.             SQL_NULL_DATA : SQL_NTS;
  379.         if (pFX->m_prs->m_nProxyParams != 0)
  380.         {
  381.             // Fill buffer (expected by SQLBindParameter) with new param data
  382.             USES_CONVERSION;
  383.             LPSTR lpszParam = (LPSTR)pFX->m_prs->m_pvParamProxy[nField-1];
  384.             lstrcpyA(lpszParam, T2A((LPCTSTR)value));
  385.         }
  386.         return;
  387. #endif // _UNICODE
  388.  
  389.     case CFieldExchange::BindFieldToColumn:
  390.         {
  391.             // Assumes all bound fields BEFORE unbound fields
  392.             CODBCFieldInfo* pODBCInfo =
  393.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  394.             UINT cbColumn = pODBCInfo->m_nPrecision;
  395.  
  396.             switch (pODBCInfo->m_nSQLType)
  397.             {
  398.             default:
  399. #ifdef _DEBUG
  400.                 // Warn of possible field schema mismatch
  401.                 if (afxTraceFlags & traceDatabase)
  402.                     TRACE1("Warning: CString converted from SQL type %ld.\n",
  403.                         pODBCInfo->m_nSQLType);
  404. #endif // _DEBUG
  405.  
  406.                 // Add room for extra information like sign, decimal point, etc.
  407.                 cbColumn += 10;
  408.                 break;
  409.  
  410.             case SQL_LONGVARCHAR:
  411.             case SQL_CHAR:
  412.             case SQL_VARCHAR:
  413.                 break;
  414.  
  415.             case SQL_FLOAT:
  416.             case SQL_REAL:
  417.             case SQL_DOUBLE:
  418.                 // Add room for sign, decimal point and " E +XXX"
  419.                 cbColumn += 10;
  420.                 break;
  421.  
  422.             case SQL_DECIMAL:
  423.             case SQL_NUMERIC:
  424.                 // Add room for sign and decimal point
  425.                 cbColumn += 2;
  426.                 break;
  427.  
  428.             case SQL_TIMESTAMP:
  429.             case SQL_DATE:
  430.             case SQL_TIME:
  431.                 // May need extra space, i.e. "{TS mm/dd/yyyy hh:mm:ss}"
  432.                 cbColumn += 10;
  433.                 break;
  434.  
  435.             case SQL_TINYINT:
  436.             case SQL_SMALLINT:
  437.             case SQL_INTEGER:
  438.             case SQL_BIGINT:
  439.                 // Add room for sign
  440.                 cbColumn += 1;
  441.                 break;
  442.             }
  443.  
  444.             // Constrain to user specified max length, subject to 256 byte min
  445.             if (cbColumn > (UINT)nMaxLength || cbColumn < 256)
  446.                 cbColumn = nMaxLength;
  447.  
  448.             // Set up binding addres
  449.             void* pvData;
  450.             value.GetBufferSetLength(cbColumn+1);
  451.             pvData = value.LockBuffer();    // will be overwritten if UNICODE
  452.  
  453. #ifdef _UNICODE
  454.             // Allocate proxy array if necessary
  455.             if (pFX->m_prs->m_pvFieldProxy == NULL)
  456.             {
  457.                 pFX->m_prs->m_pvFieldProxy = new void*[pFX->m_prs->m_nFields];
  458.                 memset(pFX->m_prs->m_pvFieldProxy, 0,
  459.                     pFX->m_prs->m_nFields*sizeof(void*));
  460.                 pFX->m_prs->m_nProxyFields = pFX->m_prs->m_nFields;
  461.             }
  462.  
  463.             // Allocate non-unicode string for SQLBindCol (not necessary on Requery)
  464.             if (pFX->m_prs->m_pvFieldProxy[nField-1] == NULL)
  465.                 pFX->m_prs->m_pvFieldProxy[nField-1] = new CHAR[cbColumn+2];
  466.  
  467.             pvData = pFX->m_prs->m_pvFieldProxy[nField-1];
  468. #endif // _UNICODE
  469.  
  470.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
  471.                 SQL_C_CHAR, pvData, cbColumn+1, plLength));
  472.             value.ReleaseBuffer();
  473.             if (!pFX->m_prs->Check(nRetCode))
  474.                 pFX->m_prs->ThrowDBException(nRetCode);
  475.  
  476.             // Add the member address to the field map
  477.             pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  478.         }
  479.         return;
  480.  
  481. #ifdef _UNICODE
  482.     case CFieldExchange::BindFieldForUpdate:
  483.         if (pFX->m_prs->m_nProxyFields != 0)
  484.         {
  485.             // Fill buffer (expected by SQLSetPos) with new field data
  486.             USES_CONVERSION;
  487.             LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  488.             lstrcpyA(lpszData, T2A((LPCTSTR)value));
  489.  
  490.             pFX->Default(szName, (void *)lpszData, plLength, SQL_C_CHAR,
  491.                 value.GetLength(), nMaxLength);
  492.         }
  493.         return;
  494. #endif // _UNICODE
  495.  
  496.     case CFieldExchange::Fixup:
  497.         if (*plLength == SQL_NULL_DATA)
  498.         {
  499.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  500.             value.GetBufferSetLength(0);
  501.             value.ReleaseBuffer();
  502.         }
  503.         else
  504.         {
  505. #ifdef _UNICODE
  506.             // Copy value out of the proxy
  507.             value = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  508. #endif
  509.  
  510.             LPTSTR lpsz = value.GetBuffer(0);
  511.             if (pFX->m_prs->m_pDatabase->m_bStripTrailingSpaces)
  512.             {
  513.                 // find first trailing space
  514.                 LPTSTR lpszFirstTrailing = NULL;
  515.                 while (*lpsz != '\0')
  516.                 {
  517.                     if (*lpsz != ' ')
  518.                         lpszFirstTrailing = NULL;
  519.                     else
  520.                     {
  521.                         if (lpszFirstTrailing == NULL)
  522.                             lpszFirstTrailing = lpsz;
  523.                     }
  524.                     lpsz = _tcsinc(lpsz);
  525.                 }
  526.                 // truncate
  527.                 if (lpszFirstTrailing != NULL)
  528.                     *lpszFirstTrailing = '\0';
  529.  
  530.             }
  531.             value.ReleaseBuffer();
  532.             *plLength = value.GetLength();
  533.         }
  534.         return;
  535.  
  536.     case CFieldExchange::SetFieldNull:
  537.         if ((pFX->m_pvField == NULL &&
  538.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  539.             pFX->m_pvField == &value)
  540.         {
  541.             if (pFX->m_bField)
  542.             {
  543.                 // Mark fields null
  544.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  545.                 // Set string 0 length
  546.                 value.GetBufferSetLength(0);
  547.                 value.ReleaseBuffer();
  548.                 *plLength = SQL_NULL_DATA;
  549.             }
  550.             else
  551.             {
  552.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  553.                 *plLength = SQL_NTS;
  554.             }
  555. #ifdef _DEBUG
  556.             pFX->m_nFieldFound = nField;
  557. #endif
  558.         }
  559.         return;
  560.  
  561.  
  562. #ifdef _UNICODE
  563.     case CFieldExchange::NameValue:
  564.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  565.         {
  566.             *pFX->m_pstr += szName;
  567.             *pFX->m_pstr += '=';
  568.         }
  569.         // Fall through
  570.  
  571.     case CFieldExchange::Value:
  572.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  573.         {
  574.             // Get the field data
  575.             CODBCFieldInfo* pODBCInfo =
  576.                     &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  577.  
  578.             LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  579.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  580.             {
  581.                 *plLength = SQL_NULL_DATA;
  582.             }
  583.             else
  584.             {
  585.                 USES_CONVERSION;
  586.                 lstrcpyA(lpszData, T2A((LPCTSTR)value));
  587.                 *plLength = value.GetLength();
  588.             }
  589.  
  590.             // If optimizing for bulk add, only need lengths & proxy set correctly
  591.             if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  592.             {
  593.                 *pFX->m_pstr += '?';
  594.                 *pFX->m_pstr += pFX->m_lpszSeparator;
  595.                 pFX->m_nParamFields++;
  596.                 AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
  597.                     (UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
  598.                     SQL_C_CHAR, pODBCInfo->m_nSQLType, nMaxLength,
  599.                     pODBCInfo->m_nScale, lpszData, 0, plLength));
  600.             }
  601.         }
  602.         return;
  603. #endif // _UNICODE
  604.  
  605.     case CFieldExchange::MarkForAddNew:
  606.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  607.         if (!value.IsEmpty())
  608.         {
  609.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  610.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  611.         }
  612.         return;
  613.  
  614.     case CFieldExchange::MarkForUpdate:
  615.         if (value.IsEmpty())
  616.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  617.         else
  618.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  619.         pFX->Default(szName, &value, plLength,
  620.             SQL_C_CHAR, value.GetLength(), nMaxLength);
  621.         return;
  622.  
  623.     case CFieldExchange::LoadField:
  624.         {
  625.             // Get the field data
  626.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  627.             CString* pStrCachedValue = (CString*)pInfo->m_pvDataCache;
  628.  
  629.             // Restore the status
  630.             pFX->m_prs->SetFieldStatus(nField - 1, pInfo->m_bStatus);
  631.  
  632.             // If not NULL, restore the value and length
  633.             if (!pFX->m_prs->IsFieldStatusNull(nField - 1))
  634.             {
  635.                 value = *pStrCachedValue;
  636.                 *plLength = value.GetLength();
  637.  
  638. #ifdef _UNICODE
  639.                 // Must restore proxy for correct WHERE CURRENT OF operation
  640.                 USES_CONVERSION;
  641.                 LPSTR lpszData = (LPSTR)pFX->m_prs->m_pvFieldProxy[nField-1];
  642.                 lstrcpyA(lpszData, T2A((LPCTSTR)value));
  643. #endif // _UNICODE
  644.             }
  645.             else
  646.             {
  647.                 *plLength = SQL_NULL_DATA;
  648.             }
  649.  
  650. #ifdef _DEBUG
  651.             // Buffer address must not change - ODBC's SQLBindCol depends upon this
  652.             void* pvBind;
  653.  
  654.     #ifdef _UNICODE
  655.             pvBind = pFX->m_prs->m_pvFieldProxy[nField-1];
  656.     #else // !_UNICODE
  657.             pvBind = value.GetBuffer(0);
  658.             value.ReleaseBuffer();
  659.     #endif
  660.  
  661.             if (pvBind != pInfo->m_pvBindAddress)
  662.             {
  663.                 TRACE1("Error: CString buffer (column %u) address has changed!\n",
  664.                     nField);
  665.                 ASSERT(FALSE);
  666.             }
  667. #endif // _DEBUG
  668.         }
  669.         return;
  670.  
  671.     case CFieldExchange::StoreField:
  672.         AfxStoreField(*pFX->m_prs, nField, &value);
  673.         return;
  674.  
  675.     case CFieldExchange::AllocCache:
  676.         {
  677.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  678.             pInfo->m_pvDataCache = new CString;
  679.             pInfo->m_nDataType = AFX_RFX_TEXT;
  680.         }
  681.         return;
  682.  
  683. #ifdef _DEBUG
  684.     case CFieldExchange::DumpField:
  685.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  686.         return;
  687. #endif // _DEBUG
  688.  
  689.     }
  690. }
  691.  
  692. void AFXAPI RFX_Int(CFieldExchange* pFX, LPCTSTR szName, int& value)
  693. {
  694.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  695.     ASSERT(AfxIsValidString(szName));
  696.  
  697.     UINT nField;
  698.     if (!pFX->IsFieldType(&nField))
  699.         return;
  700.  
  701.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  702.         nField - 1, pFX->m_nFieldType);
  703.     switch (pFX->m_nOperation)
  704.     {
  705.     case CFieldExchange::BindFieldToColumn:
  706.         {
  707. #ifdef _DEBUG
  708.             // Assumes all bound fields BEFORE unbound fields
  709.             CODBCFieldInfo* pODBCInfo =
  710.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  711.  
  712.             if (pODBCInfo->m_nSQLType != SQL_C_SHORT)
  713.             {
  714.                 // Warn of possible field schema mismatch
  715.                 if (afxTraceFlags & traceDatabase)
  716.                     TRACE1("Warning: int converted from SQL type %ld.\n",
  717.                         pODBCInfo->m_nSQLType);
  718.             }
  719. #endif // _DEBUG
  720.         }
  721.         // fall through
  722.  
  723.     default:
  724. LDefault:
  725.         pFX->Default(szName, &value, plLength, SQL_C_LONG,
  726.             sizeof(value), 5);
  727.         return;
  728.  
  729.     case CFieldExchange::Fixup:
  730.         if (*plLength == SQL_NULL_DATA)
  731.         {
  732.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  733.             value = AFX_RFX_INT_PSEUDO_NULL;
  734.         }
  735.         return;
  736.  
  737.     case CFieldExchange::SetFieldNull:
  738.         if ((pFX->m_pvField == NULL &&
  739.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  740.             pFX->m_pvField == &value)
  741.         {
  742.             if (pFX->m_bField)
  743.             {
  744.                 // Mark fields null
  745.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  746.                 value = AFX_RFX_INT_PSEUDO_NULL;
  747.                 *plLength = SQL_NULL_DATA;
  748.             }
  749.             else
  750.             {
  751.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  752.                 *plLength = sizeof(value);
  753.             }
  754. #ifdef _DEBUG
  755.             pFX->m_nFieldFound = nField;
  756. #endif
  757.         }
  758.         return;
  759.  
  760.     case CFieldExchange::MarkForAddNew:
  761.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  762.         if (!pFX->m_prs->IsFieldStatusDirty(nField - 1))
  763.         {
  764.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  765.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  766.         }
  767.         return;
  768.  
  769.     case CFieldExchange::MarkForUpdate:
  770.         if (value != AFX_RFX_INT_PSEUDO_NULL)
  771.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  772.         goto LDefault;
  773.  
  774.     case CFieldExchange::AllocCache:
  775.         {
  776.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  777.  
  778.             // Data cached by value, no allocation necessary
  779.             pInfo->m_nDataType = AFX_RFX_INT;
  780.         }
  781.         return;
  782.  
  783. #ifdef _DEBUG
  784.     case CFieldExchange::DumpField:
  785.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  786.         return;
  787. #endif // _DEBUG
  788.  
  789.     }
  790. }
  791.  
  792. void AFXAPI RFX_Long(CFieldExchange* pFX, LPCTSTR szName, long& value)
  793. {
  794.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  795.     ASSERT(AfxIsValidString(szName));
  796.  
  797.     UINT nField;
  798.     if (!pFX->IsFieldType(&nField))
  799.         return;
  800.  
  801.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  802.         nField - 1, pFX->m_nFieldType);
  803.     switch (pFX->m_nOperation)
  804.     {
  805.     case CFieldExchange::BindFieldToColumn:
  806.         {
  807. #ifdef _DEBUG
  808.             // Assumes all bound fields BEFORE unbound fields
  809.             CODBCFieldInfo* pODBCInfo =
  810.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  811.  
  812.             if (pODBCInfo->m_nSQLType != SQL_C_LONG)
  813.             {
  814.                 // Warn of possible field schema mismatch
  815.                 if (afxTraceFlags & traceDatabase)
  816.                     TRACE1("Warning: long converted from SQL type %ld.\n",
  817.                         pODBCInfo->m_nSQLType);
  818.             }
  819. #endif // _DEBUG
  820.         }
  821.         // fall through
  822.  
  823.     default:
  824. LDefault:
  825.         pFX->Default(szName, &value, plLength, SQL_C_LONG,
  826.             sizeof(value), 10);
  827.         return;
  828.  
  829.     case CFieldExchange::Fixup:
  830.         if (*plLength == SQL_NULL_DATA)
  831.         {
  832.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  833.             value = AFX_RFX_LONG_PSEUDO_NULL;
  834.         }
  835.         return;
  836.  
  837.     case CFieldExchange::SetFieldNull:
  838.         if ((pFX->m_pvField == NULL &&
  839.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  840.             pFX->m_pvField == &value)
  841.         {
  842.             if (pFX->m_bField)
  843.             {
  844.                 // Mark fields null
  845.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  846.                 value = AFX_RFX_LONG_PSEUDO_NULL;
  847.                 *plLength = SQL_NULL_DATA;
  848.             }
  849.             else
  850.             {
  851.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  852.                 *plLength = sizeof(value);
  853.             }
  854. #ifdef _DEBUG
  855.             pFX->m_nFieldFound = nField;
  856. #endif
  857.         }
  858.         return;
  859.  
  860.     case CFieldExchange::MarkForAddNew:
  861.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  862.         if (value != AFX_RFX_LONG_PSEUDO_NULL)
  863.         {
  864.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  865.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  866.         }
  867.         return;
  868.  
  869.     case CFieldExchange::MarkForUpdate:
  870.         if (value != AFX_RFX_LONG_PSEUDO_NULL)
  871.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  872.         goto LDefault;
  873.  
  874.     case CFieldExchange::AllocCache:
  875.         {
  876.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  877.  
  878.             // Data cached by value, no allocation necessary
  879.             pInfo->m_nDataType = AFX_RFX_LONG;
  880.         }
  881.         return;
  882.  
  883. #ifdef _DEBUG
  884.     case CFieldExchange::DumpField:
  885.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  886.         return;
  887. #endif // _DEBUG
  888.  
  889.     }
  890. }
  891.  
  892. void AFXAPI RFX_Byte(CFieldExchange* pFX, LPCTSTR szName, BYTE& value)
  893. {
  894.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  895.     ASSERT(AfxIsValidString(szName));
  896.  
  897.     UINT nField;
  898.     if (!pFX->IsFieldType(&nField))
  899.         return;
  900.  
  901.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  902.         nField - 1, pFX->m_nFieldType);
  903.     switch (pFX->m_nOperation)
  904.     {
  905.     case CFieldExchange::BindFieldToColumn:
  906.         {
  907. #ifdef _DEBUG
  908.             // Assumes all bound fields BEFORE unbound fields
  909.             CODBCFieldInfo* pODBCInfo =
  910.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  911.  
  912.             if (pODBCInfo->m_nSQLType != SQL_TINYINT)
  913.             {
  914.                 // Warn of possible field schema mismatch
  915.                 if (afxTraceFlags & traceDatabase)
  916.                     TRACE1("Warning: BYTE converted from SQL type %ld.\n",
  917.                         pODBCInfo->m_nSQLType);
  918.             }
  919. #endif // _DEBUG
  920.         }
  921.         // fall through
  922.  
  923.     default:
  924. LDefault:
  925.         pFX->Default(szName, &value, plLength, SQL_TINYINT,
  926.             sizeof(value), 3);
  927.         break;
  928.  
  929.     case CFieldExchange::Fixup:
  930.         if (*plLength == SQL_NULL_DATA)
  931.         {
  932.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  933.             value = AFX_RFX_BYTE_PSEUDO_NULL;
  934.         }
  935.         return;
  936.  
  937.     case CFieldExchange::SetFieldNull:
  938.         if ((pFX->m_pvField == NULL &&
  939.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  940.             pFX->m_pvField == &value)
  941.         {
  942.             if (pFX->m_bField)
  943.             {
  944.                 // Mark fields null
  945.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  946.                 value = AFX_RFX_BYTE_PSEUDO_NULL;
  947.                 *plLength = SQL_NULL_DATA;
  948.             }
  949.             else
  950.             {
  951.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  952.                 *plLength = sizeof(value);
  953.             }
  954. #ifdef _DEBUG
  955.             pFX->m_nFieldFound = nField;
  956. #endif
  957.         }
  958.         return;
  959.  
  960.     case CFieldExchange::MarkForAddNew:
  961.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  962.         if (value != AFX_RFX_BYTE_PSEUDO_NULL)
  963.         {
  964.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  965.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  966.         }
  967.         return;
  968.  
  969.     case CFieldExchange::MarkForUpdate:
  970.         if (value != AFX_RFX_BYTE_PSEUDO_NULL)
  971.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  972.         goto LDefault;
  973.  
  974.     case CFieldExchange::AllocCache:
  975.         {
  976.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  977.  
  978.             // Data cached by value, no allocation necessary
  979.             pInfo->m_nDataType = AFX_RFX_BYTE;
  980.         }
  981.         return;
  982.  
  983. #ifdef _DEBUG
  984.     case CFieldExchange::DumpField:
  985.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  986.         return;
  987. #endif // _DEBUG
  988.  
  989.     }
  990. }
  991.  
  992. void AFXAPI RFX_Bool(CFieldExchange* pFX, LPCTSTR szName, BOOL& value)
  993. {
  994.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  995.     ASSERT(AfxIsValidString(szName));
  996.  
  997.     UINT nField;
  998.     if (!pFX->IsFieldType(&nField))
  999.         return;
  1000.  
  1001.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1002.         nField - 1, pFX->m_nFieldType);
  1003.     switch (pFX->m_nOperation)
  1004.     {
  1005.     case CFieldExchange::BindFieldToColumn:
  1006.         {
  1007. #ifdef _DEBUG
  1008.             // Assumes all bound fields BEFORE unbound fields
  1009.             CODBCFieldInfo* pODBCInfo =
  1010.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1011.  
  1012.             if (pODBCInfo->m_nSQLType != SQL_BIT)
  1013.             {
  1014.                 // Warn of possible field schema mismatch
  1015.                 if (afxTraceFlags & traceDatabase)
  1016.                     TRACE1("Warning: BOOL converted from SQL type %ld.\n",
  1017.                         pODBCInfo->m_nSQLType);
  1018.             }
  1019. #endif // _DEBUG
  1020.         }
  1021.         // Fall through
  1022.  
  1023.     default:
  1024. LDefault:
  1025.         pFX->Default(szName, &value, plLength, SQL_BIT,
  1026.             sizeof(value), 1);
  1027.         return;
  1028.  
  1029.     case CFieldExchange::Fixup:
  1030.         if (*plLength == SQL_NULL_DATA)
  1031.         {
  1032.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1033.             value = AFX_RFX_BOOL_PSEUDO_NULL;
  1034.         }
  1035.         else
  1036.             // Cast BYTE into BOOL (int)
  1037.             value = *(BYTE *)&value;
  1038.         return;
  1039.  
  1040.     case CFieldExchange::SetFieldNull:
  1041.         if ((pFX->m_pvField == NULL &&
  1042.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1043.             pFX->m_pvField == &value)
  1044.         {
  1045.             if (pFX->m_bField)
  1046.             {
  1047.                 // Mark fields null
  1048.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1049.                 value = AFX_RFX_BOOL_PSEUDO_NULL;
  1050.                 *plLength = SQL_NULL_DATA;
  1051.             }
  1052.             else
  1053.             {
  1054.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1055.                 *plLength = sizeof(value);
  1056.             }
  1057. #ifdef _DEBUG
  1058.             pFX->m_nFieldFound = nField;
  1059. #endif
  1060.         }
  1061.         return;
  1062.  
  1063.     case CFieldExchange::MarkForAddNew:
  1064.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1065.         if (value != AFX_RFX_BOOL_PSEUDO_NULL)
  1066.         {
  1067.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1068.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1069.         }
  1070.         return;
  1071.  
  1072.     case CFieldExchange::MarkForUpdate:
  1073.         if (value != AFX_RFX_BOOL_PSEUDO_NULL)
  1074.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1075.         goto LDefault;
  1076.  
  1077.     case CFieldExchange::AllocCache:
  1078.         {
  1079.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1080.  
  1081.             // Data cached by value, no allocation necessary
  1082.             pInfo->m_nDataType = AFX_RFX_BOOL;
  1083.         }
  1084.         return;
  1085.  
  1086. #ifdef _DEBUG
  1087.     case CFieldExchange::DumpField:
  1088.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  1089.         return;
  1090. #endif // _DEBUG
  1091.  
  1092.     }
  1093. }
  1094.  
  1095. // Note: CByteArray.m_pData must not be changed.  This address is registered
  1096. // with ODBC and must remain valid until the recordset is released.
  1097. void AFXAPI RFX_Binary(CFieldExchange* pFX, LPCTSTR szName,
  1098.     CByteArray& value, int nMaxLength)
  1099. {
  1100.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1101.     ASSERT(AfxIsValidString(szName));
  1102.  
  1103.     RETCODE nRetCode;
  1104.     UINT nField;
  1105.     if (!pFX->IsFieldType(&nField))
  1106.         return;
  1107.  
  1108.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1109.         nField - 1, pFX->m_nFieldType);
  1110.  
  1111.     BOOL bByRef = FALSE;
  1112.     switch (pFX->m_nOperation)
  1113.     {
  1114.     default:
  1115. LDefault:
  1116.         {
  1117.             void* pvData = NULL;
  1118.             if (value.GetSize() > 0)
  1119.             {
  1120.                 if (bByRef)
  1121.                     pvData = &value;
  1122.                 else
  1123.                     pvData = &value[0];
  1124.             }
  1125.  
  1126.             pFX->Default(szName, pvData, plLength, SQL_C_BINARY,
  1127.                 (int)value.GetSize(), (UINT)value.GetSize());
  1128.         }
  1129.         return;
  1130.  
  1131.     case CFieldExchange::BindFieldToColumn:
  1132.         {
  1133.             // Assumes all bound fields BEFORE unbound fields
  1134.             CODBCFieldInfo* pODBCInfo =
  1135.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1136.             UDWORD cbColumn = pODBCInfo->m_nPrecision;
  1137.  
  1138. #ifdef _DEBUG
  1139.             if (pODBCInfo->m_nSQLType != SQL_BINARY &&
  1140.                 pODBCInfo->m_nSQLType != SQL_VARBINARY &&
  1141.                 pODBCInfo->m_nSQLType != SQL_LONGVARBINARY)
  1142.             {
  1143.                 // Warn of possible field schema mismatch
  1144.                 if (afxTraceFlags & traceDatabase)
  1145.                     TRACE1("Warning: CByteArray converted from SQL type %ld.\n",
  1146.                         pODBCInfo->m_nSQLType);
  1147.             }
  1148. #endif // _DEBUG
  1149.  
  1150.             // Constrain to user specified max length
  1151.             if (cbColumn > (UINT)nMaxLength)
  1152.                 cbColumn = nMaxLength;
  1153.             value.SetSize(cbColumn);
  1154.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
  1155.                 SQL_C_BINARY, &value[0], (LONG)cbColumn, plLength));
  1156.             if (!pFX->m_prs->Check(nRetCode))
  1157.                 pFX->m_prs->ThrowDBException(nRetCode);
  1158.  
  1159.             // Add the member address to the field map
  1160.             pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  1161.         }
  1162.         return;
  1163.  
  1164.     case CFieldExchange::Fixup:
  1165.         if (*plLength == SQL_NULL_DATA)
  1166.         {
  1167.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1168.             value.SetSize(1);
  1169.             value[0] = AFX_RFX_BYTE_PSEUDO_NULL;
  1170.         }
  1171.         else
  1172.         {
  1173.             ASSERT(*plLength <= (LONG)nMaxLength);
  1174.             ((CDBByteArray&)value).SetLength((UINT)*plLength);
  1175.         }
  1176.         return;
  1177.  
  1178.     case CFieldExchange::SetFieldNull:
  1179.         if ((pFX->m_pvField == NULL &&
  1180.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1181.             pFX->m_pvField == &value)
  1182.         {
  1183.             if (pFX->m_bField)
  1184.             {
  1185.                 // Mark fields null
  1186.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1187.                 value.SetSize(1);
  1188.                 value[0] = AFX_RFX_BYTE_PSEUDO_NULL;
  1189.                 *plLength = SQL_NULL_DATA;
  1190.             }
  1191.             else
  1192.             {
  1193.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1194.                 *plLength = value.GetSize();
  1195.             }
  1196. #ifdef _DEBUG
  1197.             pFX->m_nFieldFound = nField;
  1198. #endif
  1199.         }
  1200.         return;
  1201.  
  1202.     case CFieldExchange::MarkForAddNew:
  1203.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1204.         if (value.GetSize() != 1 || value[0] != AFX_RFX_BYTE_PSEUDO_NULL)
  1205.         {
  1206.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1207.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1208.         }
  1209.         return;
  1210.  
  1211.     case CFieldExchange::MarkForUpdate:
  1212.         if (value.GetSize() != 1 || value[0] != AFX_RFX_BYTE_PSEUDO_NULL)
  1213.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1214.         bByRef = TRUE;
  1215.         goto LDefault;
  1216.  
  1217.     case CFieldExchange::StoreField:
  1218.         AfxStoreField(*pFX->m_prs, nField, &value);
  1219.         return;
  1220.  
  1221.     case CFieldExchange::LoadField:
  1222.         AfxLoadField(*pFX->m_prs, nField, &value, plLength);
  1223.         return;
  1224.  
  1225.     case CFieldExchange::AllocCache:
  1226.         {
  1227.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1228.             pInfo->m_pvDataCache = new CByteArray;
  1229.             pInfo->m_nDataType = AFX_RFX_BINARY;
  1230.         }
  1231.         return;
  1232.  
  1233. #ifdef _DEBUG
  1234.     case CFieldExchange::DumpField:
  1235.         *pFX->m_pdcDump << "\n" << szName << ":";
  1236.         value.Dump(*pFX->m_pdcDump);
  1237.         return;
  1238. #endif // _DEBUG
  1239.  
  1240.     }
  1241. }
  1242.  
  1243. void AFXAPI RFX_Date(CFieldExchange* pFX, LPCTSTR szName, CTime& value)
  1244. {
  1245.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1246.     ASSERT(AfxIsValidString(szName));
  1247.  
  1248.     RETCODE nRetCode;
  1249.     UINT nField;
  1250.     if (!pFX->IsFieldType(&nField))
  1251.         return;
  1252.  
  1253.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1254.         nField - 1, pFX->m_nFieldType);
  1255.     switch (pFX->m_nOperation)
  1256.     {
  1257.     default:
  1258. LDefault:
  1259.         pFX->Default(szName, &value, plLength, SQL_C_TIMESTAMP,
  1260.             sizeof(value), TIMESTAMP_PRECISION);
  1261.         return;
  1262.  
  1263.     case CFieldExchange::BindParam:
  1264.         {
  1265.             TIMESTAMP_STRUCT* pts;
  1266.             pFX->m_prs->m_bRebindParams = TRUE;
  1267.  
  1268.             if (pFX->m_prs->IsParamStatusNull(nField - 1))
  1269.             {
  1270.                 pts = NULL;
  1271.                 *plLength = SQL_NULL_DATA;
  1272.             }
  1273.             else
  1274.             {
  1275.                 // Allocate proxy array if necessary
  1276.                 if (pFX->m_prs->m_pvParamProxy == NULL)
  1277.                 {
  1278.                     pFX->m_prs->m_pvParamProxy = new void*[pFX->m_prs->m_nParams];
  1279.                     memset(pFX->m_prs->m_pvParamProxy, 0,
  1280.                         pFX->m_prs->m_nParams*sizeof(void*));
  1281.                     pFX->m_prs->m_nProxyParams = pFX->m_prs->m_nParams;
  1282.                 }
  1283.  
  1284.                 // Allocate TIMESTAMP_STRUCT if necessary for SQLBindParameter
  1285.                 if (pFX->m_prs->m_pvParamProxy[nField-1] == NULL)
  1286.                 {
  1287.                     pts = new TIMESTAMP_STRUCT;
  1288.                     pFX->m_prs->m_pvParamProxy[nField-1] = pts;
  1289.                 }
  1290.                 else
  1291.                     pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvParamProxy[nField-1];
  1292.  
  1293.                 pts->year = (SWORD)value.GetYear();
  1294.                 pts->month = (UWORD)value.GetMonth();
  1295.                 pts->day = (UWORD)value.GetDay();
  1296.                 pts->hour = (UWORD)value.GetHour();
  1297.                 pts->minute = (UWORD)value.GetMinute();
  1298.                 pts->second = (UWORD)value.GetSecond();
  1299.                 pts->fraction = 0;
  1300.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  1301.             }
  1302.  
  1303.             AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt, (UWORD)nField,
  1304.                 (SWORD)pFX->m_nFieldType, SQL_C_TIMESTAMP, SQL_C_TIMESTAMP,
  1305.                 TIMESTAMP_PRECISION, 0, pts, 0, plLength));
  1306.             if (nRetCode != SQL_SUCCESS)
  1307.                 pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
  1308.  
  1309.             // Add the member address to the param map
  1310.             pFX->m_prs->m_mapParamIndex.SetAt(&value, (void*)nField);
  1311.         }
  1312.         return;
  1313.  
  1314.     case CFieldExchange::RebindParam:
  1315.         {
  1316.             *plLength = pFX->m_prs->IsParamStatusNull(nField - 1) ?
  1317.                 SQL_NULL_DATA : sizeof(TIMESTAMP_STRUCT);
  1318.             if (pFX->m_prs->m_nProxyParams != 0)
  1319.             {
  1320.                 // Fill buffer (expected by SQLBindParameter) with new param data
  1321.                 TIMESTAMP_STRUCT* pts;
  1322.                 pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvParamProxy[nField-1];
  1323.                 pts->year = (SWORD)value.GetYear();
  1324.                 pts->month = (UWORD)value.GetMonth();
  1325.                 pts->day = (UWORD)value.GetDay();
  1326.                 pts->hour = (UWORD)value.GetHour();
  1327.                 pts->minute = (UWORD)value.GetMinute();
  1328.                 pts->second = (UWORD)value.GetSecond();
  1329.                 pts->fraction = 0;
  1330.             }
  1331.         }
  1332.         return;
  1333.  
  1334.     case CFieldExchange::BindFieldToColumn:
  1335.         {
  1336. #ifdef _DEBUG
  1337.             // Assumes all bound fields BEFORE unbound fields
  1338.             CODBCFieldInfo* pODBCInfo =
  1339.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1340.  
  1341.             if (pODBCInfo->m_nSQLType != SQL_DATE &&
  1342.                 pODBCInfo->m_nSQLType != SQL_TIME &&
  1343.                 pODBCInfo->m_nSQLType != SQL_TIMESTAMP)
  1344.             {
  1345.                 // Warn of possible field schema mismatch
  1346.                 if (afxTraceFlags & traceDatabase)
  1347.                     TRACE1("Warning: CTime converted from SQL type %ld.\n",
  1348.                         pODBCInfo->m_nSQLType);
  1349.             }
  1350. #endif // _DEBUG
  1351.  
  1352.             // Allocate proxy array if necessary
  1353.             if (pFX->m_prs->m_pvFieldProxy == NULL)
  1354.             {
  1355.                 pFX->m_prs->m_pvFieldProxy = new void*[pFX->m_prs->m_nFields];
  1356.                 memset(pFX->m_prs->m_pvFieldProxy, 0,
  1357.                     pFX->m_prs->m_nFields*sizeof(void*));
  1358.                 pFX->m_prs->m_nProxyFields = pFX->m_prs->m_nFields;
  1359.             }
  1360.  
  1361.             // Allocate TIMESTAMP_STRUCT for SQLBindCol (not necessary on Requery)
  1362.             if (pFX->m_prs->m_pvFieldProxy[nField-1] == NULL)
  1363.                 pFX->m_prs->m_pvFieldProxy[nField-1] = new TIMESTAMP_STRUCT;
  1364.  
  1365.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField,
  1366.                 SQL_C_TIMESTAMP, pFX->m_prs->m_pvFieldProxy[nField-1],
  1367.                 sizeof(TIMESTAMP_STRUCT), plLength));
  1368.             if (!pFX->m_prs->Check(nRetCode))
  1369.                 pFX->m_prs->ThrowDBException(nRetCode);
  1370.  
  1371.             // Add the member address to the field map
  1372.             pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  1373.         }
  1374.         return;
  1375.  
  1376.     case CFieldExchange::BindFieldForUpdate:
  1377.         if (pFX->m_prs->m_nProxyFields != 0)
  1378.         {
  1379.             // Fill buffer (expected by SQLSetPos) with new field data
  1380.             TIMESTAMP_STRUCT* pts;
  1381.             pts = (TIMESTAMP_STRUCT *)pFX->m_prs->m_pvFieldProxy[nField-1];
  1382.             pts->year = (SWORD)value.GetYear();
  1383.             pts->month = (UWORD)value.GetMonth();
  1384.             pts->day = (UWORD)value.GetDay();
  1385.             pts->hour = (UWORD)value.GetHour();
  1386.             pts->minute = (UWORD)value.GetMinute();
  1387.             pts->second = (UWORD)value.GetSecond();
  1388.             pts->fraction = 0;
  1389.  
  1390.             pFX->Default(szName, (void *)pts, plLength, SQL_C_TIMESTAMP,
  1391.                 sizeof(TIMESTAMP_STRUCT), TIMESTAMP_PRECISION);
  1392.         }
  1393.         return;
  1394.  
  1395.     case CFieldExchange::Fixup:
  1396.         if (*plLength == SQL_NULL_DATA)
  1397.         {
  1398.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1399.             value = AFX_RFX_DATE_PSEUDO_NULL;
  1400.         }
  1401.         else
  1402.         {
  1403.             TIMESTAMP_STRUCT* pts =
  1404.                 (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  1405.             if (pts->year < 1970 || pts->year > 2038)
  1406.             {
  1407.                 // Time value out of range, return NULL
  1408. #ifdef _DEBUG
  1409.                 if (afxTraceFlags & traceDatabase)
  1410.                     TRACE0("Warning: date value out of range, returning NULL value.\n");
  1411. #endif
  1412.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1413.                 value = AFX_RFX_DATE_PSEUDO_NULL;
  1414.             }
  1415.             else
  1416.             {
  1417. #ifdef _DEBUG
  1418.                 if ((afxTraceFlags & traceDatabase) && pts->fraction != 0)
  1419.                     TRACE0("Warning: ignoring milliseconds.\n");
  1420. #endif
  1421.                 value = CTime(pts->year, pts->month, pts->day,
  1422.                     pts->hour, pts->minute, pts->second);
  1423.             }
  1424.         }
  1425.         return;
  1426.  
  1427.     case CFieldExchange::NameValue:
  1428.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  1429.         {
  1430.             *pFX->m_pstr += szName;
  1431.             *pFX->m_pstr += '=';
  1432.         }
  1433.         // Fall through
  1434.  
  1435.     case CFieldExchange::Value:
  1436.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  1437.         {
  1438.             TIMESTAMP_STRUCT* pts =
  1439.                 (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  1440.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  1441.             {
  1442.                 *plLength = SQL_NULL_DATA;
  1443.             }
  1444.             else
  1445.             {
  1446.                 pts->year = (SWORD)value.GetYear();
  1447.                 pts->month = (UWORD)value.GetMonth();
  1448.                 pts->day = (UWORD)value.GetDay();
  1449.                 pts->hour = (UWORD)value.GetHour();
  1450.                 pts->minute = (UWORD)value.GetMinute();
  1451.                 pts->second = (UWORD)value.GetSecond();
  1452.                 pts->fraction = 0;
  1453.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  1454.             }
  1455.  
  1456.             // If optimizing for bulk add, only need lengths & proxy set correctly
  1457.             if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  1458.             {
  1459.                 *pFX->m_pstr += '?';
  1460.                 *pFX->m_pstr += pFX->m_lpszSeparator;
  1461.                 pFX->m_nParamFields++;
  1462.  
  1463.                 // Assumes all bound fields BEFORE unbound fields
  1464.                 CODBCFieldInfo* pODBCInfo =
  1465.                     &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1466.  
  1467.                 AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
  1468.                     (UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
  1469.                     SQL_C_TIMESTAMP, pODBCInfo->m_nSQLType,
  1470.                     TIMESTAMP_PRECISION, 0, pts, 0, plLength));
  1471.             }
  1472.         }
  1473.         return;
  1474.  
  1475.     case CFieldExchange::SetFieldNull:
  1476.         if ((pFX->m_pvField == NULL &&
  1477.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1478.             pFX->m_pvField == &value)
  1479.         {
  1480.             if (pFX->m_bField)
  1481.             {
  1482.                 // Mark fields null
  1483.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1484.                 value = AFX_RFX_DATE_PSEUDO_NULL;
  1485.                 *plLength = SQL_NULL_DATA;
  1486.             }
  1487.             else
  1488.             {
  1489.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1490.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  1491.             }
  1492. #ifdef _DEBUG
  1493.             pFX->m_nFieldFound = nField;
  1494. #endif
  1495.         }
  1496.         return;
  1497.  
  1498.     case CFieldExchange::MarkForAddNew:
  1499.         {
  1500.             // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1501.             CTime timeNull = AFX_RFX_DATE_PSEUDO_NULL;
  1502.             if (value != timeNull)
  1503.             {
  1504.                 pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1505.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1506.             }
  1507.         }
  1508.         return;
  1509.  
  1510.     case CFieldExchange::MarkForUpdate:
  1511.         {
  1512.             CTime timeNull = AFX_RFX_DATE_PSEUDO_NULL;
  1513.             if (value != timeNull)
  1514.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1515.         }
  1516.         goto LDefault;
  1517.  
  1518.     case CFieldExchange::LoadField:
  1519.         {
  1520.             // Get the field data
  1521.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1522.  
  1523.             // Restore the status
  1524.             pFX->m_prs->SetFieldStatus(nField - 1, pInfo->m_bStatus);
  1525.  
  1526.             // If not NULL, restore the value, length and proxy
  1527.             if (!pFX->m_prs->IsFieldStatusNull(nField - 1))
  1528.             {
  1529.                 AfxCopyValueByRef(pInfo->m_pvDataCache, &value,
  1530.                     plLength, pInfo->m_nDataType);
  1531.  
  1532.                 // Restore proxy for correct WHERE CURRENT OF operations
  1533.                 TIMESTAMP_STRUCT* pts =
  1534.                     (TIMESTAMP_STRUCT*)pFX->m_prs->m_pvFieldProxy[nField-1];
  1535.  
  1536.                 pts->year = (SWORD)value.GetYear();
  1537.                 pts->month = (UWORD)value.GetMonth();
  1538.                 pts->day = (UWORD)value.GetDay();
  1539.                 pts->hour = (UWORD)value.GetHour();
  1540.                 pts->minute = (UWORD)value.GetMinute();
  1541.                 pts->second = (UWORD)value.GetSecond();
  1542.                 pts->fraction = 0;
  1543.             }
  1544.             else
  1545.                 *plLength = SQL_NULL_DATA;
  1546.  
  1547. #ifdef _DEBUG
  1548.             // Buffer address must not change - ODBC's SQLBindCol depends upon this
  1549.             if (pInfo->m_pvBindAddress != pFX->m_prs->m_pvFieldProxy[nField-1])
  1550.             {
  1551.                 TRACE1("Error: CString buffer (column %u) address has changed!\n",
  1552.                     nField);
  1553.                 ASSERT(FALSE);
  1554.             }
  1555. #endif // _DEBUG
  1556.         }
  1557.         return;
  1558.  
  1559.     case CFieldExchange::AllocCache:
  1560.         {
  1561.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1562.             pInfo->m_pvDataCache = new CTime;
  1563.             pInfo->m_nDataType = AFX_RFX_DATE;
  1564.         }
  1565.         return;
  1566.  
  1567. #ifdef _DEBUG
  1568.     case CFieldExchange::DumpField:
  1569.         *pFX->m_pdcDump << "\n" << szName << " = " << value;
  1570.         return;
  1571. #endif // _DEBUG
  1572.  
  1573.     }
  1574. }
  1575.  
  1576. void AFXAPI RFX_Date(CFieldExchange* pFX, LPCTSTR szName,
  1577.     TIMESTAMP_STRUCT& value)
  1578. {
  1579.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1580.     ASSERT(AfxIsValidString(szName));
  1581.  
  1582.     UINT nField;
  1583.     if (!pFX->IsFieldType(&nField))
  1584.         return;
  1585.  
  1586.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1587.         nField - 1, pFX->m_nFieldType);
  1588.     switch (pFX->m_nOperation)
  1589.     {
  1590.     case CFieldExchange::BindFieldToColumn:
  1591.         {
  1592. #ifdef _DEBUG
  1593.             // Assumes all bound fields BEFORE unbound fields
  1594.             CODBCFieldInfo* pODBCInfo =
  1595.                 &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1596.  
  1597.             if (pODBCInfo->m_nSQLType != SQL_DATE &&
  1598.                 pODBCInfo->m_nSQLType != SQL_TIME &&
  1599.                 pODBCInfo->m_nSQLType != SQL_TIMESTAMP)
  1600.             {
  1601.                 // Warn of possible field schema mismatch
  1602.                 if (afxTraceFlags & traceDatabase)
  1603.                     TRACE1("Warning: TIMESTAMP_STRUCT converted from SQL type %ld.\n",
  1604.                         pODBCInfo->m_nSQLType);
  1605.             }
  1606. #endif // _DEBUG
  1607.             // fall through
  1608.         }
  1609.  
  1610.     default:
  1611. LDefault:
  1612.         pFX->Default(szName, &value, plLength, SQL_C_TIMESTAMP,
  1613.             sizeof(value), TIMESTAMP_PRECISION);
  1614.         return;
  1615.  
  1616.     case CFieldExchange::Fixup:
  1617.         if (*plLength == SQL_NULL_DATA)
  1618.         {
  1619.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1620.             value.year = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1621.             value.month = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1622.             value.day = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1623.             value.hour = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1624.             value.minute = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1625.             value.second = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1626.             value.fraction = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1627.         }
  1628.         return;
  1629.  
  1630.     case CFieldExchange::SetFieldNull:
  1631.         if ((pFX->m_pvField == NULL &&
  1632.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1633.             pFX->m_pvField == &value)
  1634.         {
  1635.             if (pFX->m_bField)
  1636.             {
  1637.                 // Mark fields null
  1638.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1639.                 value.year = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1640.                 value.month = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1641.                 value.day = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1642.                 value.hour = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1643.                 value.minute = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1644.                 value.second = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1645.                 value.fraction = AFX_RFX_TIMESTAMP_PSEUDO_NULL;
  1646.                 *plLength = SQL_NULL_DATA;
  1647.             }
  1648.             else
  1649.             {
  1650.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1651.                 *plLength = sizeof(TIMESTAMP_STRUCT);
  1652.             }
  1653. #ifdef _DEBUG
  1654.             pFX->m_nFieldFound = nField;
  1655. #endif
  1656.         }
  1657.         return;
  1658.  
  1659.     case CFieldExchange::MarkForAddNew:
  1660.         // can force writing of psuedo-null value (as a non-null) by setting field dirty
  1661.         if (!(value.year == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1662.             value.month == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1663.             value.day == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1664.             value.hour == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1665.             value.minute == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1666.             value.second == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1667.             value.fraction == AFX_RFX_TIMESTAMP_PSEUDO_NULL ))
  1668.         {
  1669.             pFX->m_prs->SetDirtyFieldStatus(nField - 1);
  1670.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1671.         }
  1672.         return;
  1673.  
  1674.     case CFieldExchange::MarkForUpdate:
  1675.         if (!(value.year == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1676.             value.month == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1677.             value.day == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1678.             value.hour == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1679.             value.minute == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1680.             value.second == AFX_RFX_TIMESTAMP_PSEUDO_NULL &&
  1681.             value.fraction == AFX_RFX_TIMESTAMP_PSEUDO_NULL ))
  1682.         {
  1683.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1684.         }
  1685.         goto LDefault;
  1686.  
  1687.     case CFieldExchange::AllocCache:
  1688.         {
  1689.             CFieldInfo* pInfo = &pFX->m_prs->m_rgFieldInfos[nField - 1];
  1690.             pInfo->m_pvDataCache = new TIMESTAMP_STRUCT;
  1691.             pInfo->m_nDataType = AFX_RFX_TIMESTAMP;
  1692.         }
  1693.         return;
  1694.  
  1695. #ifdef _DEBUG
  1696.     case CFieldExchange::DumpField:
  1697.         *pFX->m_pdcDump << "\n" << szName << ".year = " << (int)value.year;
  1698.         *pFX->m_pdcDump << "\n" << szName << ".month = " << value.month;
  1699.         *pFX->m_pdcDump << "\n" << szName << ".day = " << value.day;
  1700.         *pFX->m_pdcDump << "\n" << szName << ".hour = " << value.hour;
  1701.         *pFX->m_pdcDump << "\n" << szName << ".minute = " << value.minute;
  1702.         *pFX->m_pdcDump << "\n" << szName << ".second = " << value.second;
  1703.         *pFX->m_pdcDump << "\n" << szName << ".fraction = " << value.fraction;
  1704.         return;
  1705. #endif // _DEBUG
  1706.  
  1707.     }
  1708. }
  1709.  
  1710. void AFXAPI RFX_LongBinary(CFieldExchange* pFX, LPCTSTR szName,
  1711.     CLongBinary& value)
  1712. {
  1713.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  1714.     ASSERT(AfxIsValidString(szName));
  1715.  
  1716.     RETCODE nRetCode;
  1717.     UINT nField;
  1718.     if (!pFX->IsFieldType(&nField))
  1719.         return;
  1720.  
  1721.     LONG* plLength = pFX->m_prs->GetFieldLengthBuffer(
  1722.         nField - 1, pFX->m_nFieldType);
  1723.     switch (pFX->m_nOperation)
  1724.     {
  1725.     case CFieldExchange::Name:
  1726.         pFX->m_prs->m_bLongBinaryColumns = TRUE;
  1727.         pFX->Default(szName, &value, plLength, SQL_C_DEFAULT, 0, 0);
  1728.         return;
  1729.  
  1730.     case CFieldExchange::BindFieldToColumn:
  1731.         // Don't bind if using update SQL, simply do SQLGetData on Fixup
  1732.         if (!pFX->m_prs->m_bUseUpdateSQL && pFX->m_prs->CanUpdate())
  1733.         {
  1734.             // Bind for updates with cb=0 now. Driver may not support post Execute or ExtendedFetch binding
  1735.             AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt, (UWORD)nField, SQL_C_DEFAULT,
  1736.                 &value, 0, plLength));
  1737.             if (!pFX->m_prs->Check(nRetCode))
  1738.                 pFX->m_prs->ThrowDBException(nRetCode);
  1739.         }
  1740.  
  1741.         // Add the member address to the field map
  1742.         pFX->m_prs->m_mapFieldIndex.SetAt(&value, (void*)nField);
  1743.         return;
  1744.  
  1745. #ifdef _DEBUG
  1746.     case CFieldExchange::BindParam:
  1747.         // CLongBinary parameters are not supported
  1748.         ASSERT(FALSE);
  1749.  
  1750.     case CFieldExchange::MarkForAddNew:
  1751.     case CFieldExchange::MarkForUpdate:
  1752.         // We do not archive LongBinary values
  1753.     case CFieldExchange::StoreField:
  1754.     case CFieldExchange::LoadField:
  1755.         // We do not archive LongBinary values
  1756. #endif // _DEBUG
  1757.     default:
  1758.         return;
  1759.  
  1760.     case CFieldExchange::Fixup:
  1761.         // Get the size of the long binary field
  1762.         *plLength = pFX->GetLongBinarySize(nField);
  1763.  
  1764.         // Get the data if necessary
  1765.         if (*plLength != SQL_NULL_DATA)
  1766.             pFX->GetLongBinaryData(nField, value, plLength);
  1767.  
  1768.         // Set the status and length
  1769.         if (*plLength == SQL_NULL_DATA)
  1770.         {
  1771.             // Field NULL, set length and status
  1772.             value.m_dwDataLength = 0;
  1773.             pFX->m_prs->SetNullFieldStatus(nField - 1);
  1774.         }
  1775.         else
  1776.         {
  1777.             // Field not NULL, clear the status (length already set)
  1778.             pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1779.         }
  1780.  
  1781.         return;
  1782.  
  1783.     case CFieldExchange::NameValue:
  1784.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  1785.         {
  1786.             *pFX->m_pstr += szName;
  1787.             *pFX->m_pstr += '=';
  1788.         }
  1789.  
  1790.         // Fall through
  1791.     case CFieldExchange::Value:
  1792.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  1793.         {
  1794.             // If user marked column NULL, reflect this in length
  1795.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  1796.                 *plLength = SQL_NULL_DATA;
  1797.             else
  1798.             {
  1799.                 // Indicate data will be sent after SQLExecute
  1800.                 // Length is signed value, it's limited by LONG_MAX
  1801.                 if (value.m_dwDataLength >
  1802.                     (ULONG)(LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET)))
  1803.                 {
  1804.                     ASSERT(FALSE);
  1805.                     *plLength = LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET);
  1806.                 }
  1807.                 else
  1808.                     *plLength = value.m_dwDataLength;
  1809.  
  1810.                 *plLength = SQL_LEN_DATA_AT_EXEC(*plLength);
  1811.             }
  1812.  
  1813.             // If optimizing for bulk add, only need lengths set correctly
  1814.             if(!(pFX->m_prs->m_dwOptions & CRecordset::optimizeBulkAdd))
  1815.             {
  1816.                 *pFX->m_pstr += '?';
  1817.                 *pFX->m_pstr += pFX->m_lpszSeparator;
  1818.                 pFX->m_nParamFields++;
  1819.  
  1820.                 // Assumes all bound fields BEFORE unbound fields
  1821.                 CODBCFieldInfo* pODBCInfo =
  1822.                     &pFX->m_prs->m_rgODBCFieldInfos[nField - 1];
  1823.  
  1824.                 AFX_SQL_SYNC(::SQLBindParameter(pFX->m_hstmt,
  1825.                     (UWORD)pFX->m_nParamFields, SQL_PARAM_INPUT,
  1826.                     SQL_C_DEFAULT, pODBCInfo->m_nSQLType,
  1827.                     value.m_dwDataLength, 0, &value, 0, plLength));
  1828.                 if (nRetCode != SQL_SUCCESS)
  1829.                     pFX->m_prs->ThrowDBException(nRetCode, pFX->m_hstmt);
  1830.             }
  1831.         }
  1832.         return;
  1833.  
  1834.     case CFieldExchange::BindFieldForUpdate:
  1835.         if (pFX->m_prs->IsFieldStatusDirty(nField - 1))
  1836.         {
  1837.             // If user marked column NULL, reflect this in length
  1838.             if (pFX->m_prs->IsFieldStatusNull(nField - 1))
  1839.                 *plLength = SQL_NULL_DATA;
  1840.             else
  1841.             {
  1842.                 // Length is signed value, it's limited by LONG_MAX
  1843.                 if (value.m_dwDataLength >
  1844.                     (ULONG)(LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET)))
  1845.                 {
  1846.                     ASSERT(FALSE);
  1847.                     *plLength = LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET);
  1848.                 }
  1849.                 else
  1850.                     *plLength = value.m_dwDataLength;
  1851.  
  1852.                 *plLength = SQL_LEN_DATA_AT_EXEC(*plLength);
  1853.             }
  1854.         }
  1855.         else
  1856.             *plLength = SQL_IGNORE;
  1857.  
  1858.         return;
  1859.  
  1860.     case CFieldExchange::UnbindFieldForUpdate:
  1861.         *plLength = value.m_dwDataLength;
  1862.         return;
  1863.  
  1864.     case CFieldExchange::SetFieldNull:
  1865.         if ((pFX->m_pvField == NULL &&
  1866.             pFX->m_nFieldType == CFieldExchange::outputColumn) ||
  1867.             pFX->m_pvField == &value)
  1868.         {
  1869.             if (pFX->m_bField)
  1870.             {
  1871.                 // Mark fields null
  1872.                 pFX->m_prs->SetNullFieldStatus(nField - 1);
  1873.                 value.m_dwDataLength = 0;
  1874.                 *plLength = SQL_NULL_DATA;
  1875.             }
  1876.             else
  1877.             {
  1878.                 pFX->m_prs->ClearNullFieldStatus(nField - 1);
  1879.  
  1880.                 // Length is signed value, it's limited by LONG_MAX
  1881.                 if (value.m_dwDataLength >
  1882.                     (ULONG)(LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET)))
  1883.                 {
  1884.                     ASSERT(FALSE);
  1885.                     *plLength = LONG_MAX - labs(SQL_LEN_DATA_AT_EXEC_OFFSET);
  1886.                 }
  1887.                 else
  1888.                     *plLength = value.m_dwDataLength;
  1889.  
  1890.                 *plLength = SQL_LEN_DATA_AT_EXEC(*plLength);
  1891.             }
  1892. #ifdef _DEBUG
  1893.             pFX->m_nFieldFound = nField;
  1894. #endif
  1895.         }
  1896.         return;
  1897.  
  1898.     case CFieldExchange::AllocCache:
  1899.         // Caching not supported for long binary
  1900.         return;
  1901.  
  1902. #ifdef _DEBUG
  1903.     case CFieldExchange::DumpField:
  1904.         *pFX->m_pdcDump << "\n" << szName << " = ";
  1905.         value.Dump(*pFX->m_pdcDump);
  1906.         return;
  1907. #endif // _DEBUG
  1908.  
  1909.     }
  1910. }
  1911.  
  1912. long CFieldExchange::GetLongBinarySize(int nField)
  1913. {
  1914.     RETCODE nRetCode;
  1915.     int nDummy;
  1916.     long lSize;
  1917.  
  1918.     // Give empty buffer to find size of entire LongBinary
  1919.     AFX_ODBC_CALL(::SQLGetData(m_prs->m_hstmt,
  1920.         (UWORD)nField, SQL_C_DEFAULT, &nDummy, 0, &lSize));
  1921.  
  1922.     switch (nRetCode)
  1923.     {
  1924.         case SQL_SUCCESS:
  1925.             break;
  1926.  
  1927.         case SQL_SUCCESS_WITH_INFO:
  1928. #ifdef _DEBUG
  1929.             if (afxTraceFlags & traceDatabase)
  1930.             {
  1931.                 CDBException* e = new CDBException(nRetCode);
  1932.                 e->BuildErrorString(m_prs->m_pDatabase,
  1933.                     m_prs->m_hstmt, FALSE);
  1934.  
  1935.                 // Ignore data truncated messages
  1936.                 if (e->m_strStateNativeOrigin.Find(_T("State:01004")) < 0)
  1937.                 {
  1938.                     TRACE0("Warning: ODBC Success With Info, ");
  1939.                     e->TraceErrorMessage(e->m_strError);
  1940.                     e->TraceErrorMessage(e->m_strStateNativeOrigin);
  1941.                 }
  1942.                 e->Delete();
  1943.             }
  1944. #endif // _DEBUG
  1945.             break;
  1946.  
  1947.         default:
  1948.             m_prs->ThrowDBException(nRetCode);
  1949.     }
  1950.  
  1951.     return lSize;
  1952. }
  1953.  
  1954. void CFieldExchange::GetLongBinaryData(int nField, CLongBinary& lb,
  1955.     long* plSize)
  1956. {
  1957.     RETCODE nRetCode;
  1958.     long lActualDataSize = 0;
  1959.     long lChunkDataSize;
  1960.     long lReallocSize;
  1961.     const BYTE* lpLongBinary;
  1962.  
  1963.     lb.m_dwDataLength = 0;
  1964.  
  1965.     // Determine initial chunk sizes
  1966.     if (*plSize == SQL_NO_TOTAL)
  1967.     {
  1968.         lChunkDataSize = m_lDefaultLBFetchSize;
  1969.         lReallocSize = m_lDefaultLBReallocSize;
  1970.     }
  1971.     else
  1972.     {
  1973.         lChunkDataSize = *plSize;
  1974.         lReallocSize = *plSize;
  1975.     }
  1976.  
  1977.     do
  1978.     {
  1979.         // Check if CLongBianary is big enough
  1980.         lpLongBinary = ReallocLongBinary(lb,
  1981.             (long)lb.m_dwDataLength + lChunkDataSize,
  1982.             (long)lb.m_dwDataLength + lReallocSize);
  1983.  
  1984.         // Adjust the pointer so that data added at end
  1985.         lpLongBinary += lb.m_dwDataLength;
  1986.  
  1987.         AFX_ODBC_CALL(::SQLGetData(m_prs->m_hstmt, (UWORD)nField,
  1988.             SQL_C_BINARY, (UCHAR*)lpLongBinary, lChunkDataSize, &lActualDataSize));
  1989.         ::GlobalUnlock(lb.m_hData);
  1990.  
  1991.         switch (nRetCode)
  1992.         {
  1993.             case SQL_NO_DATA_FOUND:
  1994.                 m_prs->SetNullFieldStatus(nField - 1);
  1995.                 *plSize = SQL_NULL_DATA;
  1996.                 break;
  1997.  
  1998.             case SQL_SUCCESS:
  1999.                 // All data fetched
  2000.                 lb.m_dwDataLength += lActualDataSize;
  2001.                 *plSize = (long)lb.m_dwDataLength;
  2002.                 return;
  2003.  
  2004.             case SQL_SUCCESS_WITH_INFO:
  2005.                 {
  2006.                     CDBException* e = new CDBException(nRetCode);
  2007.                     e->BuildErrorString(m_prs->m_pDatabase, m_prs->m_hstmt,
  2008.                         FALSE);
  2009.  
  2010.                     // Ignore data truncated messages
  2011.                     if (e->m_strStateNativeOrigin.Find(_T("State:01004")) < 0)
  2012.                     {
  2013. #ifdef _DEBUG
  2014.                         if (afxTraceFlags & traceDatabase)
  2015.                         {
  2016.                             TRACE0("Warning: ODBC Success With Info, ");
  2017.                             e->TraceErrorMessage(e->m_strError);
  2018.                             e->TraceErrorMessage(e->m_strStateNativeOrigin);
  2019.                         }
  2020. #endif // _DEBUG
  2021.  
  2022.                         // Must be some other warning, should be finished
  2023.                         lb.m_dwDataLength += lActualDataSize;
  2024.                     }
  2025.                     else
  2026.                     {
  2027.                         // Should only happen if SQL_NO_TOTAL
  2028.  
  2029.                         // Increment the length by the chunk size for subsequent SQLGetData call
  2030.                         lb.m_dwDataLength += lChunkDataSize;
  2031.  
  2032.                         // Recalculate chunk and alloc sizes
  2033.                         lChunkDataSize = m_prs->GetLBFetchSize(lChunkDataSize);
  2034.                         lReallocSize = m_prs->GetLBReallocSize(lReallocSize);
  2035.                     }
  2036.  
  2037.                     *plSize = (long)lb.m_dwDataLength;
  2038.                     e->Delete();
  2039.                 }
  2040.                 break;
  2041.  
  2042.             default:
  2043.                 m_prs->ThrowDBException(nRetCode);
  2044.         }
  2045.  
  2046.     } while (lActualDataSize == SQL_NO_TOTAL);
  2047. }
  2048.  
  2049. BYTE* CFieldExchange::ReallocLongBinary(CLongBinary& lb, long lSizeRequired,
  2050.     long lReallocSize)
  2051. {
  2052.     // realloc max of lSizeRequired, lReallocSize (m_dwDataLength untouched)
  2053.  
  2054.     if (lSizeRequired < 0)
  2055.     {
  2056.         ASSERT(FALSE);
  2057.         lSizeRequired = 0;
  2058.     }
  2059.  
  2060.     HGLOBAL hOldData = NULL;
  2061.  
  2062.     // Allocate or Realloc as req'd
  2063.     if (lb.m_hData == NULL)
  2064.         lb.m_hData = ::GlobalAlloc(GMEM_MOVEABLE, lReallocSize);
  2065.     else
  2066.     {
  2067.         DWORD dwSize = ::GlobalSize(lb.m_hData);
  2068.         if (dwSize < (DWORD)lSizeRequired)
  2069.         {
  2070.             // Save the old handle in case ReAlloc fails
  2071.             hOldData = lb.m_hData;
  2072.  
  2073.             // Allocate more memory if necessary
  2074.             lb.m_hData = ::GlobalReAlloc(lb.m_hData,
  2075.                 __max(lSizeRequired, lReallocSize), GMEM_MOVEABLE);
  2076.         }
  2077.     }
  2078.  
  2079.     // Validate the memory was allocated and can be locked
  2080.     if (lb.m_hData == NULL)
  2081.     {
  2082.         // Restore the old handle (not NULL if Realloc failed)
  2083.         lb.m_hData = hOldData;
  2084.         AfxThrowMemoryException();
  2085.     }
  2086.  
  2087.     BYTE* lpLongBinary = (BYTE*)::GlobalLock(lb.m_hData);
  2088.     if (lpLongBinary == NULL)
  2089.     {
  2090.         ::GlobalFree(lb.m_hData);
  2091.         lb.m_hData = NULL;
  2092.         AfxThrowMemoryException();
  2093.     }
  2094.  
  2095.     return lpLongBinary;
  2096. }
  2097.  
  2098. //////////////////////////////////////////////////////////////////////////////
  2099. // Recordset Field Exchange Helpers
  2100.  
  2101.  
  2102. void AFXAPI AfxStoreField(CRecordset& rs, UINT nField, void* pvField)
  2103. {
  2104.     // Get the field data
  2105.     CFieldInfo* pInfo = &rs.m_rgFieldInfos[nField - 1];
  2106.  
  2107.     // Cache the status
  2108.     pInfo->m_bStatus = rs.GetFieldStatus(nField - 1);
  2109.  
  2110.     // Save the data
  2111.     if (!rs.IsFieldStatusNull(nField - 1))
  2112.     {
  2113.         // Don't need to save length for variable len
  2114.         // objects as CString and CByteArray track len internally
  2115.         long nDummyLength;
  2116.  
  2117.         if (pInfo->m_nDataType == AFX_RFX_BOOL ||
  2118.             pInfo->m_nDataType == AFX_RFX_BYTE ||
  2119.             pInfo->m_nDataType == AFX_RFX_INT ||
  2120.             pInfo->m_nDataType == AFX_RFX_LONG ||
  2121.             pInfo->m_nDataType == AFX_RFX_SINGLE)
  2122.         {
  2123.             // If caching data by value, pass a ref
  2124.             AfxCopyValueByRef(pvField, &pInfo->m_pvDataCache,
  2125.                 &nDummyLength, pInfo->m_nDataType);
  2126.         }
  2127.         else
  2128.         {
  2129.             AfxCopyValueByRef(pvField, pInfo->m_pvDataCache,
  2130.                 &nDummyLength, pInfo->m_nDataType);
  2131.         }
  2132.     }
  2133.  
  2134. #ifdef _DEBUG
  2135.     // Cache the bind address expected by ODBC
  2136.     switch(pInfo->m_nDataType)
  2137.     {
  2138.     default:
  2139.         // All types that are bound directly
  2140.         pInfo->m_pvBindAddress = pvField;
  2141.         break;
  2142.  
  2143.     case AFX_RFX_TEXT:
  2144. #ifdef _UNICODE
  2145.         pInfo->m_pvBindAddress = rs.m_pvFieldProxy[nField-1];
  2146. #else
  2147.         pInfo->m_pvBindAddress = ((CString*)pvField)->GetBuffer(0);
  2148.         ((CString*)pvField)->ReleaseBuffer();
  2149. #endif
  2150.         break;
  2151.  
  2152.     case AFX_RFX_DATE:
  2153.         pInfo->m_pvBindAddress = rs.m_pvFieldProxy[nField-1];
  2154.         break;
  2155.  
  2156.     case AFX_RFX_BINARY:
  2157.         pInfo->m_pvBindAddress = ((CByteArray*)pvField)->GetData();
  2158.         break;
  2159.     }
  2160. #endif
  2161. }
  2162.  
  2163. void AFXAPI AfxLoadField(CRecordset& rs, UINT nField,
  2164.     void* pvField, long* plLength)
  2165. {
  2166.     // Get the field data
  2167.     CFieldInfo* pInfo = &rs.m_rgFieldInfos[nField - 1];
  2168.  
  2169.     // Assumes old field status cleared out
  2170.     rs.SetFieldStatus(nField - 1, pInfo->m_bStatus);
  2171.  
  2172.     // If not NULL, restore the value and the length
  2173.     if (!rs.IsFieldStatusNull(nField - 1))
  2174.     {
  2175.         if (pInfo->m_nDataType == AFX_RFX_BOOL ||
  2176.             pInfo->m_nDataType == AFX_RFX_BYTE ||
  2177.             pInfo->m_nDataType == AFX_RFX_INT ||
  2178.             pInfo->m_nDataType == AFX_RFX_LONG ||
  2179.             pInfo->m_nDataType == AFX_RFX_SINGLE)
  2180.         {
  2181.             // If caching data by value, pass a ref
  2182.             AfxCopyValueByRef(&pInfo->m_pvDataCache, pvField,
  2183.                 plLength, pInfo->m_nDataType);
  2184.         }
  2185.         else
  2186.         {
  2187.             AfxCopyValueByRef(pInfo->m_pvDataCache, pvField,
  2188.                 plLength, pInfo->m_nDataType);
  2189.         }
  2190.     }
  2191.     else
  2192.         *plLength = SQL_NULL_DATA;
  2193.  
  2194. #ifdef _DEBUG
  2195.     // Buffer address must not change - ODBC's SQLBindCol depends upon this
  2196.     if (pInfo->m_nDataType == AFX_RFX_BINARY)
  2197.     {
  2198.         // Change pvField to point to the data of the CByteArray
  2199.         pvField = ((CByteArray*)pvField)->GetData();
  2200.     }
  2201.  
  2202.     if (pInfo->m_pvBindAddress != pvField)
  2203.     {
  2204.         TRACE1("Error: field address (column %u) has changed!\n",
  2205.             nField);
  2206.         ASSERT(FALSE);
  2207.     }
  2208. #endif // _DEBUG
  2209. }
  2210.  
  2211. BOOL AFXAPI AfxCompareValueByRef(void* pvSrc, void* pvDest, int nSrcType)
  2212. {
  2213.     ASSERT(pvSrc != NULL);
  2214.     ASSERT(pvDest != NULL);
  2215.  
  2216.     BOOL bCompare = FALSE;
  2217.  
  2218.     switch(nSrcType)
  2219.     {
  2220.  
  2221.     case AFX_RFX_LONGBINARY:
  2222.         // Caching long binary Src not supported
  2223.     default:
  2224.         ASSERT(FALSE);
  2225.         break;
  2226.  
  2227.     case AFX_RFX_TEXT:
  2228.         if (*(CString*)pvDest == *(CString*)pvSrc)
  2229.             bCompare = TRUE;
  2230.         break;
  2231.  
  2232.     case AFX_RFX_BINARY:
  2233.         {
  2234.             CByteArray* pByteArraySrc = (CByteArray*)pvSrc;
  2235.             CByteArray* pByteArrayDest = (CByteArray*)pvDest;
  2236.  
  2237.             // If sizes compare, compare the Src
  2238.             int nSize = pByteArraySrc->GetSize();
  2239.             if (nSize == pByteArrayDest->GetSize())
  2240.             {
  2241.                 if (memcmp(&pByteArrayDest[0], &pByteArraySrc[0], nSize) == 0)
  2242.                     bCompare = TRUE;
  2243.             }
  2244.         }
  2245.         break;
  2246.     case AFX_RFX_BOOL:
  2247.         if (*(BOOL*)pvDest == *(BOOL*)pvSrc)
  2248.             bCompare = TRUE;
  2249.         break;
  2250.  
  2251.     case AFX_RFX_BYTE:
  2252.         if (*(BYTE*)pvDest == *(BYTE*)pvSrc)
  2253.             bCompare = TRUE;
  2254.         break;
  2255.  
  2256.     case AFX_RFX_INT:
  2257.         if (*(int*)pvDest == *(int*)pvSrc)
  2258.             bCompare = TRUE;
  2259.         break;
  2260.  
  2261.     case AFX_RFX_LONG:
  2262.         if (*(long*)pvDest == *(long*)pvSrc)
  2263.             bCompare = TRUE;
  2264.         break;
  2265.  
  2266.     case AFX_RFX_SINGLE:
  2267.         if (*(float*)pvDest == *(float*)pvSrc)
  2268.             bCompare = TRUE;
  2269.         break;
  2270.  
  2271.     case AFX_RFX_DOUBLE:
  2272.         if (*(double*)pvDest == *(double*)pvSrc)
  2273.             bCompare = TRUE;
  2274.         break;
  2275.  
  2276.     case AFX_RFX_DATE:
  2277.         if (*(CTime*)pvDest == *(CTime*)pvSrc)
  2278.             bCompare = TRUE;
  2279.         break;
  2280.  
  2281.     case AFX_RFX_TIMESTAMP:
  2282.         {
  2283.             TIMESTAMP_STRUCT* pSrc = (TIMESTAMP_STRUCT*)pvSrc;
  2284.             TIMESTAMP_STRUCT* pDest = (TIMESTAMP_STRUCT*)pvDest;
  2285.             if ((pSrc->year == pDest->year &&
  2286.                 pSrc->month == pDest->month &&
  2287.                 pSrc->day == pDest->day &&
  2288.                 pSrc->hour == pDest->hour &&
  2289.                 pSrc->minute == pDest->minute &&
  2290.                 pSrc->second == pDest->second &&
  2291.                 pSrc->fraction == pDest->fraction))
  2292.             {
  2293.                 bCompare = TRUE;
  2294.             }
  2295.         }
  2296.         break;
  2297.     }
  2298.  
  2299.     return bCompare;
  2300. }
  2301.  
  2302. void AFXAPI AfxCopyValueByRef(void* pvSrc, void* pvDest, long* plLength, int nDestType)
  2303. {
  2304.     ASSERT(pvSrc != NULL);
  2305.     ASSERT(pvDest != NULL);
  2306.     ASSERT(plLength != NULL);
  2307.  
  2308.     switch (nDestType)
  2309.     {
  2310.     case AFX_RFX_LONGBINARY:
  2311.         // Caching long binary Dest not supported
  2312.     default:
  2313.         ASSERT(FALSE);
  2314.         break;
  2315.  
  2316.     case AFX_RFX_TEXT:
  2317.         *(CString*)pvDest = *(CString*)pvSrc;
  2318.         *plLength = ((CString*)pvSrc)->GetLength();
  2319.         break;
  2320.  
  2321.     case AFX_RFX_BINARY:
  2322.         ((CByteArray*)pvDest)->Copy(*(CByteArray*)pvSrc);
  2323.         *plLength = ((CByteArray*)pvSrc)->GetSize();
  2324.         break;
  2325.  
  2326.     case AFX_RFX_BOOL:
  2327.         *(BOOL*)pvDest = *(BOOL*)pvSrc;
  2328.         *plLength = sizeof(BOOL);
  2329.         break;
  2330.  
  2331.     case AFX_RFX_BYTE:
  2332.         *(BYTE*)pvDest = *(BYTE*)pvSrc;
  2333.         *plLength = sizeof(BYTE);
  2334.         break;
  2335.  
  2336.     case AFX_RFX_INT:
  2337.         *(int*)pvDest = *(int*)pvSrc;
  2338.         *plLength = sizeof(int);
  2339.         break;
  2340.  
  2341.     case AFX_RFX_LONG:
  2342.         *(long*)pvDest = *(long*)pvSrc;
  2343.         *plLength = sizeof(long);
  2344.         break;
  2345.  
  2346.     case AFX_RFX_SINGLE:
  2347.         *(float*)pvDest = *(float*)pvSrc;
  2348.         *plLength = sizeof(float);
  2349.         break;
  2350.  
  2351.     case AFX_RFX_DOUBLE:
  2352.         *(double*)pvDest = *(double*)pvSrc;
  2353.         *plLength = sizeof(double);
  2354.         break;
  2355.  
  2356.     case AFX_RFX_DATE:
  2357.         *(CTime*)pvDest = *(CTime*)pvSrc;
  2358.         *plLength = sizeof(TIMESTAMP_STRUCT);
  2359.         break;
  2360.  
  2361.     case AFX_RFX_TIMESTAMP:
  2362.         {
  2363.             TIMESTAMP_STRUCT* pDest = (TIMESTAMP_STRUCT*)pvDest;
  2364.             TIMESTAMP_STRUCT* pSrc = (TIMESTAMP_STRUCT*)pvSrc;
  2365.  
  2366.             pDest->year = pSrc->year;
  2367.             pDest->month = pSrc->month;
  2368.             pDest->day = pSrc->day;
  2369.             pDest->hour = pSrc->hour;
  2370.             pDest->minute = pSrc->minute;
  2371.             pDest->second = pSrc->second;
  2372.             pDest->fraction = pSrc->fraction;
  2373.  
  2374.             *plLength = sizeof(TIMESTAMP_STRUCT);
  2375.         }
  2376.         break;
  2377.     }
  2378. }
  2379.  
  2380. //////////////////////////////////////////////////////////////////////////////
  2381. // Bulk Recordset Field Exchange
  2382.  
  2383. void AFXAPI RFX_Text_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  2384.     LPSTR* prgStrVals, long** prgLengths, int nMaxLength)
  2385. {
  2386.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2387.     ASSERT(AfxIsValidString(szName));
  2388.  
  2389.     UINT nField;
  2390.     if (!pFX->IsFieldType(&nField))
  2391.         return;
  2392.  
  2393.     switch (pFX->m_nOperation)
  2394.     {
  2395.     case CFieldExchange::AllocMultiRowBuffer:
  2396.         {
  2397.             // The buffer pointer better be initialized to NULL
  2398.             // or cleanup in exceptional cases mail fail
  2399.             ASSERT(*prgStrVals == NULL);
  2400.             ASSERT(*prgLengths == NULL);
  2401.  
  2402.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  2403.  
  2404.             // Allocate buffers to hold data and length
  2405.             *prgStrVals = new char[nRowsetSize * nMaxLength];
  2406.             *prgLengths = new long[nRowsetSize];
  2407.         }
  2408.         break;
  2409.  
  2410.     case CFieldExchange::DeleteMultiRowBuffer:
  2411.         delete [] *prgStrVals;
  2412.         *prgStrVals = NULL;
  2413.  
  2414.         delete [] *prgLengths;
  2415.         *prgLengths = NULL;
  2416.         break;
  2417.  
  2418.     default:
  2419.         AfxRFXBulkDefault(pFX, szName, *prgStrVals, *prgLengths,
  2420.             SQL_C_CHAR, nMaxLength);
  2421.         break;
  2422.     }
  2423. }
  2424.  
  2425. void AFXAPI RFX_Bool_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  2426.     BOOL** prgBoolVals, long** prgLengths)
  2427. {
  2428.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2429.     ASSERT(AfxIsValidString(szName));
  2430.  
  2431.     UINT nField;
  2432.     if (!pFX->IsFieldType(&nField))
  2433.         return;
  2434.  
  2435.     switch (pFX->m_nOperation)
  2436.     {
  2437.     case CFieldExchange::AllocMultiRowBuffer:
  2438.         {
  2439.             // The buffer pointer better be initialized to NULL
  2440.             // or cleanup in exceptional cases mail fail
  2441.             ASSERT(*prgBoolVals == NULL);
  2442.             ASSERT(*prgLengths == NULL);
  2443.  
  2444.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  2445.  
  2446.             // Allocate buffers to hold data and length
  2447.             *prgBoolVals = new BOOL[nRowsetSize];
  2448.             *prgLengths = new long[nRowsetSize];
  2449.         }
  2450.         break;
  2451.  
  2452.     case CFieldExchange::DeleteMultiRowBuffer:
  2453.         delete [] *prgBoolVals;
  2454.         *prgBoolVals = NULL;
  2455.  
  2456.         delete [] *prgLengths;
  2457.         *prgLengths = NULL;
  2458.         break;
  2459.  
  2460.     default:
  2461.         AfxRFXBulkDefault(pFX, szName, *prgBoolVals, *prgLengths,
  2462.             SQL_C_LONG, sizeof(BOOL));
  2463.         break;
  2464.     }
  2465. }
  2466.  
  2467. void AFXAPI RFX_Int_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  2468.     int** prgIntVals, long** prgLengths)
  2469. {
  2470.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2471.     ASSERT(AfxIsValidString(szName));
  2472.  
  2473.     UINT nField;
  2474.     if (!pFX->IsFieldType(&nField))
  2475.         return;
  2476.  
  2477.     switch (pFX->m_nOperation)
  2478.     {
  2479.     case CFieldExchange::AllocMultiRowBuffer:
  2480.         {
  2481.             // The buffer pointer better be initialized to NULL
  2482.             // or cleanup in exceptional cases mail fail
  2483.             ASSERT(*prgIntVals == NULL);
  2484.             ASSERT(*prgLengths == NULL);
  2485.  
  2486.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  2487.  
  2488.             // Allocate buffers to hold data and length
  2489.             *prgIntVals = new int[nRowsetSize];
  2490.             *prgLengths = new long[nRowsetSize];
  2491.         }
  2492.         break;
  2493.  
  2494.     case CFieldExchange::DeleteMultiRowBuffer:
  2495.         delete [] *prgIntVals;
  2496.         *prgIntVals = NULL;
  2497.  
  2498.         delete [] *prgLengths;
  2499.         *prgLengths = NULL;
  2500.         break;
  2501.  
  2502.     default:
  2503.         AfxRFXBulkDefault(pFX, szName, *prgIntVals, *prgLengths,
  2504.             SQL_C_LONG, sizeof(int));
  2505.         break;
  2506.     }
  2507. }
  2508.  
  2509. void AFXAPI RFX_Long_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  2510.     long** prgLongVals, long** prgLengths)
  2511. {
  2512.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2513.     ASSERT(AfxIsValidString(szName));
  2514.  
  2515.     UINT nField;
  2516.     if (!pFX->IsFieldType(&nField))
  2517.         return;
  2518.  
  2519.     switch (pFX->m_nOperation)
  2520.     {
  2521.     case CFieldExchange::AllocMultiRowBuffer:
  2522.         {
  2523.             // The buffer pointer better be initialized to NULL
  2524.             // or cleanup in exceptional cases mail fail
  2525.             ASSERT(*prgLongVals == NULL);
  2526.             ASSERT(*prgLengths == NULL);
  2527.  
  2528.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  2529.  
  2530.             // Allocate buffers to hold data and length
  2531.             *prgLongVals = new long[nRowsetSize];
  2532.             *prgLengths = new long[nRowsetSize];
  2533.         }
  2534.         break;
  2535.  
  2536.     case CFieldExchange::DeleteMultiRowBuffer:
  2537.         delete [] *prgLongVals;
  2538.         *prgLongVals = NULL;
  2539.  
  2540.         delete [] *prgLengths;
  2541.         *prgLengths = NULL;
  2542.         break;
  2543.  
  2544.     default:
  2545.         AfxRFXBulkDefault(pFX, szName, *prgLongVals, *prgLengths,
  2546.             SQL_C_LONG, sizeof(long));
  2547.         break;
  2548.     }
  2549. }
  2550.  
  2551. void AFXAPI RFX_Date_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  2552.     TIMESTAMP_STRUCT** prgTSVals, long** prgLengths)
  2553. {
  2554.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2555.     ASSERT(AfxIsValidString(szName));
  2556.  
  2557.     UINT nField;
  2558.     if (!pFX->IsFieldType(&nField))
  2559.         return;
  2560.  
  2561.     switch (pFX->m_nOperation)
  2562.     {
  2563.     case CFieldExchange::AllocMultiRowBuffer:
  2564.         {
  2565.             // The buffer pointer better be initialized to NULL
  2566.             // or cleanup in exceptional cases mail fail
  2567.             ASSERT(*prgTSVals == NULL);
  2568.             ASSERT(*prgLengths == NULL);
  2569.  
  2570.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  2571.  
  2572.             // Allocate buffers to hold data and length
  2573.             *prgTSVals = new TIMESTAMP_STRUCT[nRowsetSize];
  2574.             *prgLengths = new long[nRowsetSize];
  2575.         }
  2576.         break;
  2577.  
  2578.     case CFieldExchange::DeleteMultiRowBuffer:
  2579.         delete [] *prgTSVals;
  2580.         *prgTSVals = NULL;
  2581.  
  2582.         delete [] *prgLengths;
  2583.         *prgLengths = NULL;
  2584.         break;
  2585.  
  2586.     default:
  2587.         AfxRFXBulkDefault(pFX, szName, *prgTSVals, *prgLengths,
  2588.             SQL_C_TIMESTAMP, sizeof(TIMESTAMP_STRUCT));
  2589.         break;
  2590.     }
  2591. }
  2592.  
  2593. void AFXAPI RFX_Byte_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  2594.     BYTE** prgByteVals, long** prgLengths)
  2595. {
  2596.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2597.     ASSERT(AfxIsValidString(szName));
  2598.  
  2599.     UINT nField;
  2600.     if (!pFX->IsFieldType(&nField))
  2601.         return;
  2602.  
  2603.     switch (pFX->m_nOperation)
  2604.     {
  2605.     case CFieldExchange::AllocMultiRowBuffer:
  2606.         {
  2607.             // The buffer pointer better be initialized to NULL
  2608.             // or cleanup in exceptional cases mail fail
  2609.             ASSERT(*prgByteVals == NULL);
  2610.             ASSERT(*prgLengths == NULL);
  2611.  
  2612.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  2613.  
  2614.             // Allocate buffers to hold data and length
  2615.             *prgByteVals = new BYTE[nRowsetSize];
  2616.             *prgLengths = new long[nRowsetSize];
  2617.         }
  2618.         break;
  2619.  
  2620.     case CFieldExchange::DeleteMultiRowBuffer:
  2621.         delete [] *prgByteVals;
  2622.         *prgByteVals = NULL;
  2623.  
  2624.         delete [] *prgLengths;
  2625.         *prgLengths = NULL;
  2626.         break;
  2627.  
  2628.     default:
  2629.         AfxRFXBulkDefault(pFX, szName, *prgByteVals, *prgLengths,
  2630.             SQL_C_TINYINT, sizeof(BYTE));
  2631.         break;
  2632.     }
  2633. }
  2634.  
  2635. void AFXAPI RFX_Binary_Bulk(CFieldExchange* pFX, LPCTSTR szName,
  2636.     BYTE** prgByteVals, long** prgLengths, int nMaxLength)
  2637. {
  2638.     ASSERT(AfxIsValidAddress(pFX, sizeof(CFieldExchange)));
  2639.     ASSERT(AfxIsValidString(szName));
  2640.  
  2641.     UINT nField;
  2642.     if (!pFX->IsFieldType(&nField))
  2643.         return;
  2644.  
  2645.     switch (pFX->m_nOperation)
  2646.     {
  2647.     case CFieldExchange::AllocMultiRowBuffer:
  2648.         {
  2649.             // The buffer pointer better be initialized to NULL
  2650.             // or cleanup in exceptional cases mail fail
  2651.             ASSERT(*prgByteVals == NULL);
  2652.             ASSERT(*prgLengths == NULL);
  2653.  
  2654.             int nRowsetSize = pFX->m_prs->GetRowsetSize();
  2655.  
  2656.             // Allocate buffers to hold data and length
  2657.             *prgByteVals = new BYTE[nRowsetSize * nMaxLength];
  2658.             *prgLengths = new long[nRowsetSize];
  2659.         }
  2660.         break;
  2661.  
  2662.     case CFieldExchange::DeleteMultiRowBuffer:
  2663.         delete [] *prgByteVals;
  2664.         *prgByteVals = NULL;
  2665.  
  2666.         delete [] *prgLengths;
  2667.         *prgLengths = NULL;
  2668.         break;
  2669.  
  2670.     default:
  2671.         AfxRFXBulkDefault(pFX, szName, *prgByteVals, *prgLengths,
  2672.             SQL_C_BINARY, nMaxLength);
  2673.         break;
  2674.     }
  2675. }
  2676.  
  2677. void AFXAPI AfxRFXBulkDefault(CFieldExchange* pFX, LPCTSTR szName,
  2678.     void* pv, long* rgLengths, int nCType, UINT cbValue)
  2679. {
  2680.     RETCODE nRetCode;
  2681.  
  2682.     switch(pFX->m_nOperation)
  2683.     {
  2684.     default:
  2685.         // Operation not valid for bulk fetch
  2686.         ASSERT(FALSE);
  2687.         return;
  2688.  
  2689.     case CFieldExchange::Name:
  2690.         // We require a name
  2691.         ASSERT(lstrlen(szName) != 0);
  2692.  
  2693.         *pFX->m_pstr += szName;
  2694.         *pFX->m_pstr += pFX->m_lpszSeparator;
  2695.         break;
  2696.  
  2697.     case CFieldExchange::BindFieldToColumn:
  2698.         AFX_SQL_SYNC(::SQLBindCol(pFX->m_prs->m_hstmt,
  2699.             (UWORD)pFX->m_nFields, (SWORD)nCType, pv, cbValue, rgLengths));
  2700.         if (!pFX->m_prs->Check(nRetCode))
  2701.             pFX->m_prs->ThrowDBException(nRetCode);
  2702.         break;
  2703.     }
  2704. }
  2705.  
  2706. //////////////////////////////////////////////////////////////////////////////
  2707. // Inline function declarations expanded out-of-line
  2708.  
  2709. #ifndef _AFX_ENABLE_INLINES
  2710.  
  2711. static char _szAfxDbInl[] = "afxdb.inl";
  2712. #undef THIS_FILE
  2713. #define THIS_FILE _szAfxDbInl
  2714. #define _AFXDBRFX_INLINE
  2715. #include "afxdb.inl"
  2716.  
  2717. #endif
  2718.  
  2719. #ifdef AFX_INIT_SEG
  2720. #pragma code_seg(AFX_INIT_SEG)
  2721. #endif
  2722.  
  2723. /////////////////////////////////////////////////////////////////////////////
  2724.